/***************************************************************************
 *   Copyright (c) 2010 Jürgen Riegel <juergen.riegel@web.de>              *
 *                                                                         *
 *   This file is part of the FreeCAD CAx development system.              *
 *                                                                         *
 *   This library is free software; you can redistribute it and/or         *
 *   modify it under the terms of the GNU Library General Public           *
 *   License as published by the Free Software Foundation; either          *
 *   version 2 of the License, or (at your option) any later version.      *
 *                                                                         *
 *   This library  is distributed in the hope that it will be useful,      *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU Library General Public License for more details.                  *
 *                                                                         *
 *   You should have received a copy of the GNU Library General Public     *
 *   License along with this library; see the file COPYING.LIB. If not,    *
 *   write to the Free Software Foundation, Inc., 59 Temple Place,         *
 *   Suite 330, Boston, MA  02111-1307, USA                                *
 *                                                                         *
 ***************************************************************************/


#include "pch.h"
#include <QtGui/qguiapplication.h>
#include <QtWidgets/qaction.h>
#include <QtWidgets/QApplication>
#include <boost_signals2.hpp>
#ifndef _PreComp_
# include <Inventor/events/SoKeyboardEvent.h>
# include <Inventor/nodes/SoPickStyle.h>
# include <QtWidgets/QApplication>
# include <QtWidgets/QInputDialog>
# include <QtWidgets/QMessageBox>
# include <QtCore/QString>
# include <cstdlib>
# include <qdebug.h>
# include <GC_MakeEllipse.hxx>
# include <boost/math/special_functions/fpclassify.hpp>
# include <memory>
#endif
# include <boost/math/special_functions/fpclassify.hpp>

#include <Base/Console.h>
#include <Base/Exception.h>
#include <Base/Tools.h>

#include <App/OriginFeature.h>
#include <Gui/Action.h>
#include <Gui/Application.h>
#include <Gui/BitmapFactory.h>
#include <Gui/Document.h>
#include <Gui/CommandT.h>
#include <Gui/MainWindow.h>
//#include <Gui/DlgEditFileIncludePropertyExternal.h>
#include <Gui/Selection.h>
#include <Gui/SelectionFilter.h>
#include <Sketcher/App/SketchObject.h>
// #include <Part/App/DatumFeature.h>
// #include <Part/App/BodyBase.h>
#include <Part/App/Geometry2d.h>
#include <Sketcher/App/Constraint.h>

#include "ViewProviderSketch.h"
#include "DrawSketchHandler.h"
#include "Utils.h"

#include <Gui/View3DInventor.h>
#include <Gui/View3DInventorViewer.h>
#include <Gui/SoFCUnifiedSelection.h>

// #include <Gui/ToolBarManager.h>

#include <GC_MakeArcOfCircle.hxx>
#include <GC_MakeCircle.hxx>
#include <GC_MakeSegment.hxx>

#include "GeometryCreationMode.h"
#include <App/Application.h>
#include <App/Document.h>
// #include "SketcherRegularPolygonDialog.h"
#include "BuildGeoImp.h"
using namespace std;
using namespace SketcherGui;


#define CONSTRUCTION_UPDATE_ACTION(CLASS, ICON)                                                             \
    /* This macro creates an updateAction() function that will toggle between normal & construction icon */ \
    void CLASS::updateAction(int mode)                                                                      \
    {                                                                                                       \
        auto act = getAction();                                                                             \
        if (act) {                                                                                          \
            switch (mode) {                                                                                 \
                case Normal:                                                                                \
                    act->setIcon(Gui::BitmapFactory().iconFromTheme(ICON));                                 \
                    break;                                                                                  \
                case Construction:                                                                          \
                    act->setIcon(Gui::BitmapFactory().iconFromTheme(ICON "_Constr"));                       \
                    break;                                                                                  \
            }                                                                                               \
        }                                                                                                   \
    }

namespace SketcherGui {
GeometryCreationMode geometryCreationMode=Normal;
}

/* helper functions ======================================================*/

// Return counter-clockwise angle from horizontal out of p1 to p2 in radians.
double GetPointAngle (const Base::Vector2d &p1, const Base::Vector2d &p2)
{
  double dX = p2.x - p1.x;
  double dY = p2.y - p1.y;
  return dY >= 0 ? atan2(dY, dX) : atan2(dY, dX) + 2*M_PI;
}

void ActivateHandler(Gui::Document *doc, DrawSketchHandler *handler)
{
    std::unique_ptr<DrawSketchHandler> ptr(handler);
    if (doc) {
        if (doc->getInEdit() && doc->getInEdit()->isDerivedFrom(SketcherGui::ViewProviderSketch::getClassTypeId())) {
            SketcherGui::ViewProviderSketch* vp = static_cast<SketcherGui::ViewProviderSketch*> (doc->getInEdit());
            vp->purgeHandler();
            vp->activateHandler(ptr.release());
        }
    }
}

bool isCreateGeoActive(Gui::Document *doc)
{
    if (doc) {
        // checks if a Sketch Viewprovider is in Edit and is in no special mode
        if (doc->getInEdit() && doc->getInEdit()->isDerivedFrom
            (SketcherGui::ViewProviderSketch::getClassTypeId())) {
            /*if (dynamic_cast<SketcherGui::ViewProviderSketch*>(doc->getInEdit())->
                getSketchMode() == ViewProviderSketch::STATUS_NONE)*/
                return true;
        }
    }
    return false;
}

SketcherGui::ViewProviderSketch* getSketchViewprovider(Gui::Document *doc)
{
    if (doc) {
        if (doc->getInEdit() && doc->getInEdit()->isDerivedFrom
            (SketcherGui::ViewProviderSketch::getClassTypeId()) )
            return dynamic_cast<SketcherGui::ViewProviderSketch*>(doc->getInEdit());
    }
    return nullptr;
}

void removeRedundantHorizontalVertical(Sketcher::SketchObject* psketch,
                                       std::vector<AutoConstraint> &sug1,
                                       std::vector<AutoConstraint> &sug2)
{
    if(!sug1.empty() && !sug2.empty()) {

        bool rmvhorvert = false;

        // we look for:
        // 1. Coincident to external on both endpoints
        // 2. Coincident in one endpoint to origin and pointonobject/tangent to an axis on the other
        auto detectredundant = [psketch](std::vector<AutoConstraint> &sug, bool &ext, bool &orig, bool &axis) {

            ext = false;
            orig = false;
            axis = false;

            for(std::vector<AutoConstraint>::const_iterator it = sug.begin(); it!=sug.end(); ++it) {
                if( (*it).Type == Sketcher::Coincident && ext == false) {
                    const std::map<int, Sketcher::PointPos> coincidents = psketch->getAllCoincidentPoints((*it).GeoId, (*it).PosId);

                    if(!coincidents.empty()) {
                        // the keys are ordered, so if the first is negative, it is coincident with external
                        ext = coincidents.begin()->first < 0;

                        std::map<int, Sketcher::PointPos>::const_iterator geoId1iterator;

                        geoId1iterator = coincidents.find(-1);

                        if( geoId1iterator != coincidents.end()) {
                            if( (*geoId1iterator).second == Sketcher::PointPos::start )
                                orig = true;
                        }
                    }
                    else { // it may be that there is no constraint at all, but there is external geometry
                        ext = (*it).GeoId < 0;
                        orig = ((*it).GeoId == -1 && (*it).PosId == Sketcher::PointPos::start);
                    }
                }
                else if( (*it).Type == Sketcher::PointOnObject && axis == false) {
                    axis = (((*it).GeoId == -1 && (*it).PosId == Sketcher::PointPos::none) || ((*it).GeoId == -2 && (*it).PosId == Sketcher::PointPos::none));
                }

            }
        };

        bool firstext = false, secondext = false, firstorig = false, secondorig = false, firstaxis = false, secondaxis = false;

        detectredundant(sug1, firstext, firstorig, firstaxis);
        detectredundant(sug2, secondext, secondorig, secondaxis);


        rmvhorvert = ((firstext && secondext)   ||  // coincident with external on both endpoints
                      (firstorig && secondaxis) ||  // coincident origin and point on object on other
                      (secondorig && firstaxis));

        if(rmvhorvert) {
            for(std::vector<AutoConstraint>::reverse_iterator it = sug2.rbegin(); it!=sug2.rend(); ++it) {
                if( (*it).Type == Sketcher::Horizontal || (*it).Type == Sketcher::Vertical) {
                    sug2.erase(std::next(it).base());
                    it = sug2.rbegin(); // erase invalidates the iterator
                }
            }
        }
    }
}


/* Sketch commands =======================================================*/



class DrawSketchHandlerLine: public DrawSketchHandler
{
public:
    DrawSketchHandlerLine():Mode(STATUS_SEEK_First),EditCurve(2){}
    virtual ~DrawSketchHandlerLine(){}
    /// mode table
    enum SelectMode {
        STATUS_SEEK_First,      /**< enum value ----. */
        STATUS_SEEK_Second,     /**< enum value ----. */
        STATUS_End
    };

    virtual void mouseMove(Base::Vector2d onSketchPos) override
    {
        if (Mode==STATUS_SEEK_First) {
            setPositionText(onSketchPos);
            if (seekAutoConstraint(sugConstr1, onSketchPos, Base::Vector2d(0.f,0.f))) {
                renderSuggestConstraintsCursor(sugConstr1);
                return;
            }
        }
        else if (Mode==STATUS_SEEK_Second){
            float length = (onSketchPos - EditCurve[0]).Length();
            float angle = (onSketchPos - EditCurve[0]).GetAngle(Base::Vector2d(1.f,0.f));
            SbString text;
            text.sprintf(" (%.1f,%.1fdeg)", length, angle * 180 / M_PI);
            setPositionText(onSketchPos, text);

            EditCurve[1] = onSketchPos;
            drawEdit(EditCurve);
            if (seekAutoConstraint(sugConstr2, onSketchPos, onSketchPos - EditCurve[0])) {
                renderSuggestConstraintsCursor(sugConstr2);
                return;
            }
        }
        applyCursor();
    }

    virtual bool pressButton(Base::Vector2d onSketchPos) override
    {
        if (Mode==STATUS_SEEK_First){
            EditCurve[0] = onSketchPos;

            Mode = STATUS_SEEK_Second;
        }
        else {
            EditCurve[1] = onSketchPos;
            drawEdit(EditCurve);
            Mode = STATUS_End;
        }
        return true;
    }

    virtual bool releaseButton(Base::Vector2d onSketchPos) override
    {
        Q_UNUSED(onSketchPos);
        if (Mode==STATUS_End){
            unsetCursor();
            resetPositionText();

            try {
                Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Add sketch line"));
                Gui::cmdAppObjectArgs(sketchgui->getObject(), "addGeometry(Part.LineSegment(App.Vector(%f,%f,0),App.Vector(%f,%f,0)),%s)",
                          EditCurve[0].x,EditCurve[0].y,EditCurve[1].x,EditCurve[1].y,
                          geometryCreationMode==Construction?"True":"False");
                
                Part::GeomLineSegment* seg  = create_segment(EditCurve[0],EditCurve[1]);
                static_cast<Sketcher::SketchObject*>(sketchgui->getObject())->addGeometry(seg,geometryCreationMode==Construction);
                Gui::Command::commitCommand();
            }
            catch (const Base::Exception& e) {
                Base::Console().Error("Failed to add line: %s\n", e.what());
                Gui::Command::abortCommand();
            }

            ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Mod/Sketcher");
            bool avoidredundant = sketchgui->AvoidRedundant.getValue()  && sketchgui->Autoconstraints.getValue();

            if(avoidredundant)
                removeRedundantHorizontalVertical(static_cast<Sketcher::SketchObject *>(sketchgui->getObject()),sugConstr1,sugConstr2);

            // add auto constraints for the line segment start
            if (!sugConstr1.empty()) {
                createAutoConstraints(sugConstr1, getHighestCurveIndex(), Sketcher::PointPos::start);
                sugConstr1.clear();
            }

            // add auto constraints for the line segment end
            if (!sugConstr2.empty()) {
                createAutoConstraints(sugConstr2, getHighestCurveIndex(), Sketcher::PointPos::end);
                sugConstr2.clear();
            }

            tryAutoRecomputeIfNotSolve(static_cast<Sketcher::SketchObject *>(sketchgui->getObject()));

            EditCurve.clear();
            drawEdit(EditCurve);

            bool continuousMode = hGrp->GetBool("ContinuousCreationMode",true);
            if(continuousMode){
                // This code enables the continuous creation mode.
                Mode=STATUS_SEEK_First;
                EditCurve.resize(2);
                applyCursor();
                /* It is ok not to call to purgeHandler
                * in continuous creation mode because the
                * handler is destroyed by the quit() method on pressing the
                * right button of the mouse */
            }
            else{
                sketchgui->purgeHandler(); // no code after this line, Handler get deleted in ViewProvider
            }
        }
        return true;
    }

private:

    virtual void activated() override
    {
        setCrosshairCursor("Sketcher_Pointer_Create_Line");
    }

protected:
    SelectMode Mode;
    std::vector<Base::Vector2d> EditCurve;
    std::vector<AutoConstraint> sugConstr1, sugConstr2;
};

DEF_STD_CMD_AU(CmdSketcherCreateLine)

CmdSketcherCreateLine::CmdSketcherCreateLine()
  : Command("Sketcher_CreateLine")
{
    sAppModule      = "Sketcher";
    sGroup          = "Sketcher";
    sMenuText       = QT_TR_NOOP("Create line");
    sToolTipText    = QT_TR_NOOP("Create a line in the sketch");
    sWhatsThis      = "Sketcher_CreateLine";
    sStatusTip      = sToolTipText;
    sPixmap         = "Sketcher_CreateLine";
    sAccel          = "G, L";
    eType           = ForEdit;
}

CONSTRUCTION_UPDATE_ACTION(CmdSketcherCreateLine, "Sketcher_CreateLine")

void CmdSketcherCreateLine::activated(int iMsg)
{
    Q_UNUSED(iMsg);
    ActivateHandler(getActiveGuiDocument(),new DrawSketchHandlerLine() );
}

bool CmdSketcherCreateLine::isActive(void)
{
    return isCreateGeoActive(getActiveGuiDocument());
}


/* Create Box =======================================================*/

class DrawSketchHandlerBox: public DrawSketchHandler
{
public:
    enum ConstructionMethod {
        Diagonal,
        CenterAndCorner
    };

    DrawSketchHandlerBox(ConstructionMethod constrMethod = Diagonal):   Mode(STATUS_SEEK_First),
                                                                        EditCurve(5),
                                                                        constructionMethod(constrMethod){}
    virtual ~DrawSketchHandlerBox(){}

    /// mode table
    enum BoxMode {
        STATUS_SEEK_First,      /**< enum value ----. */
        STATUS_SEEK_Second,     /**< enum value ----. */
        STATUS_End
    };

public:

    virtual void mouseMove(Base::Vector2d onSketchPos) override
    {

        if (Mode==STATUS_SEEK_First) {
            setPositionText(onSketchPos);
            if (seekAutoConstraint(sugConstr1, onSketchPos, Base::Vector2d(0.f,0.f))) {
                renderSuggestConstraintsCursor(sugConstr1);
                return;
            }
        }
        else if (Mode==STATUS_SEEK_Second) {
            if(constructionMethod == Diagonal) {
                float dx = onSketchPos.x - EditCurve[0].x;
                float dy = onSketchPos.y - EditCurve[0].y;
                SbString text;
                text.sprintf(" (%.1f x %.1f)", dx, dy);
                setPositionText(onSketchPos, text);

                EditCurve[2] = onSketchPos;
                EditCurve[1] = Base::Vector2d(onSketchPos.x ,EditCurve[0].y);
                EditCurve[3] = Base::Vector2d(EditCurve[0].x,onSketchPos.y);

            }
            else if (constructionMethod == CenterAndCorner) {
                float dx = onSketchPos.x - center.x;
                float dy = onSketchPos.y - center.y;
                SbString text;
                text.sprintf(" (%.1f x %.1f)", dx, dy);
                setPositionText(onSketchPos, text);

                EditCurve[0] = center - (onSketchPos - center);
                EditCurve[1] = Base::Vector2d(EditCurve[0].x,onSketchPos.y);
                EditCurve[2] = onSketchPos;
                EditCurve[3] = Base::Vector2d(onSketchPos.x,EditCurve[0].y);
                EditCurve[4] = EditCurve[0];
            }

            drawEdit(EditCurve);
            if (seekAutoConstraint(sugConstr2, onSketchPos, Base::Vector2d(0.0,0.0))) {
                renderSuggestConstraintsCursor(sugConstr2);
                return;
            }

        }
        applyCursor();
    }

    virtual bool pressButton(Base::Vector2d onSketchPos) override
    {
        if (Mode==STATUS_SEEK_First){
            if(constructionMethod == Diagonal) {
                EditCurve[0] = onSketchPos;
                EditCurve[4] = onSketchPos;
            }
            else if (constructionMethod == CenterAndCorner) {
                center = onSketchPos;
            }

            Mode = STATUS_SEEK_Second;
        }
        else {
            if(constructionMethod == Diagonal) {
                EditCurve[2] = onSketchPos;
                EditCurve[1] = Base::Vector2d(onSketchPos.x ,EditCurve[0].y);
                EditCurve[3] = Base::Vector2d(EditCurve[0].x,onSketchPos.y);
                drawEdit(EditCurve);
                Mode = STATUS_End;
            }
            else if (constructionMethod == CenterAndCorner) {
                EditCurve[0] = center - (onSketchPos - center);
                EditCurve[1] = Base::Vector2d(EditCurve[0].x,onSketchPos.y);
                EditCurve[2] = onSketchPos;
                EditCurve[3] = Base::Vector2d(onSketchPos.x,EditCurve[0].y);
                EditCurve[4] = EditCurve[0];
                drawEdit(EditCurve);
                Mode = STATUS_End;
            }
        }
        return true;
    }

    virtual bool releaseButton(Base::Vector2d onSketchPos) override
    {
        Q_UNUSED(onSketchPos);
        if (Mode==STATUS_End){
            unsetCursor();
            resetPositionText();
            int firstCurve = getHighestCurveIndex() + 1;

            try {
                if(constructionMethod == Diagonal) {
                    Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Add sketch box"));
                    Gui::Command::doCommand(Gui::Command::Doc,
                        "geoList = []\n"
                        "geoList.append(Part.LineSegment(App.Vector(%f,%f,0),App.Vector(%f,%f,0)))\n"
                        "geoList.append(Part.LineSegment(App.Vector(%f,%f,0),App.Vector(%f,%f,0)))\n"
                        "geoList.append(Part.LineSegment(App.Vector(%f,%f,0),App.Vector(%f,%f,0)))\n"
                        "geoList.append(Part.LineSegment(App.Vector(%f,%f,0),App.Vector(%f,%f,0)))\n"
                        "%s.addGeometry(geoList,%s)\n"
                        "conList = []\n"
                        "conList.append(Sketcher.Constraint('Coincident',%i,2,%i,1))\n"
                        "conList.append(Sketcher.Constraint('Coincident',%i,2,%i,1))\n"
                        "conList.append(Sketcher.Constraint('Coincident',%i,2,%i,1))\n"
                        "conList.append(Sketcher.Constraint('Coincident',%i,2,%i,1))\n"
                        "conList.append(Sketcher.Constraint('Horizontal',%i))\n"
                        "conList.append(Sketcher.Constraint('Horizontal',%i))\n"
                        "conList.append(Sketcher.Constraint('Vertical',%i))\n"
                        "conList.append(Sketcher.Constraint('Vertical',%i))\n"
                        "%s.addConstraint(conList)\n"
                        "del geoList, conList\n",
                        EditCurve[0].x,EditCurve[0].y,EditCurve[1].x,EditCurve[1].y, // line 1
                        EditCurve[1].x,EditCurve[1].y,EditCurve[2].x,EditCurve[2].y, // line 2
                        EditCurve[2].x,EditCurve[2].y,EditCurve[3].x,EditCurve[3].y, // line 3
                        EditCurve[3].x,EditCurve[3].y,EditCurve[0].x,EditCurve[0].y, // line 4
                        Gui::Command::getObjectCmd(sketchgui->getObject()).c_str(), // the sketch
                        geometryCreationMode==Construction?"True":"False", // geometry as construction or not
                        firstCurve,firstCurve+1, // coincident1
                        firstCurve+1,firstCurve+2, // coincident2
                        firstCurve+2,firstCurve+3, // coincident3
                        firstCurve+3,firstCurve, // coincident4
                        firstCurve, // horizontal1
                        firstCurve+2, // horizontal2
                        firstCurve+1, // vertical1
                        firstCurve+3, // vertical2
                        Gui::Command::getObjectCmd(sketchgui->getObject()).c_str()); // the sketch

                        Gui::Command::commitCommand();
                }
                else if (constructionMethod == CenterAndCorner) {
                    Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Add centered sketch box"));
                    Gui::Command::doCommand(Gui::Command::Doc,
                        "geoList = []\n"
                        "geoList.append(Part.LineSegment(App.Vector(%f,%f,0),App.Vector(%f,%f,0)))\n"
                        "geoList.append(Part.LineSegment(App.Vector(%f,%f,0),App.Vector(%f,%f,0)))\n"
                        "geoList.append(Part.LineSegment(App.Vector(%f,%f,0),App.Vector(%f,%f,0)))\n"
                        "geoList.append(Part.LineSegment(App.Vector(%f,%f,0),App.Vector(%f,%f,0)))\n"
                        "geoList.append(Part.Point(App.Vector(%f,%f,0)))\n"
                        "%s.addGeometry(geoList,%s)\n"
                        "conList = []\n"
                        "conList.append(Sketcher.Constraint('Coincident',%i,2,%i,1))\n"
                        "conList.append(Sketcher.Constraint('Coincident',%i,2,%i,1))\n"
                        "conList.append(Sketcher.Constraint('Coincident',%i,2,%i,1))\n"
                        "conList.append(Sketcher.Constraint('Coincident',%i,2,%i,1))\n"
                        "conList.append(Sketcher.Constraint('Horizontal',%i))\n"
                        "conList.append(Sketcher.Constraint('Horizontal',%i))\n"
                        "conList.append(Sketcher.Constraint('Vertical',%i))\n"
                        "conList.append(Sketcher.Constraint('Vertical',%i))\n"
                        "conList.append(Sketcher.Constraint('Symmetric',%i,2,%i,1,%i,1))\n"
                        "%s.addConstraint(conList)\n"
                        "del geoList, conList\n",
                        EditCurve[0].x,EditCurve[0].y,EditCurve[1].x,EditCurve[1].y, // line 1
                        EditCurve[1].x,EditCurve[1].y,EditCurve[2].x,EditCurve[2].y, // line 2
                        EditCurve[2].x,EditCurve[2].y,EditCurve[3].x,EditCurve[3].y, // line 3
                        EditCurve[3].x,EditCurve[3].y,EditCurve[0].x,EditCurve[0].y, // line 4
                        center.x,center.y,                                           // center point
                        Gui::Command::getObjectCmd(sketchgui->getObject()).c_str(), // the sketch
                        geometryCreationMode==Construction?"True":"False", // geometry as construction or not
                        firstCurve,firstCurve+1, // coincident1
                        firstCurve+1,firstCurve+2, // coincident2
                        firstCurve+2,firstCurve+3, // coincident3
                        firstCurve+3,firstCurve, // coincident4
                        firstCurve+1, // horizontal1
                        firstCurve+3, // horizontal2
                        firstCurve, // vertical1
                        firstCurve+2, // vertical2
                        firstCurve+1, firstCurve, firstCurve + 4, // Symmetric
                        Gui::Command::getObjectCmd(sketchgui->getObject()).c_str()); // the sketch

                        Gui::Command::commitCommand();
                }
            }
            catch (const Base::Exception& e) {
                Base::Console().Error("Failed to add box: %s\n", e.what());
                Gui::Command::abortCommand();
            }

            if(constructionMethod == Diagonal) {
                // add auto constraints at the start of the first side
                if (sugConstr1.size() > 0) {
                    createAutoConstraints(sugConstr1, getHighestCurveIndex() - 3 , Sketcher::PointPos::start);
                    sugConstr1.clear();
                }

                // add auto constraints at the end of the second side
                if (sugConstr2.size() > 0) {
                    createAutoConstraints(sugConstr2, getHighestCurveIndex() - 2, Sketcher::PointPos::end);
                    sugConstr2.clear();
                }

            }
            else if (constructionMethod == CenterAndCorner) {
                // add auto constraints at the start of the first side
                if (sugConstr1.size() > 0) {
                    createAutoConstraints(sugConstr1, getHighestCurveIndex(), Sketcher::PointPos::start);
                    sugConstr1.clear();
                }

                // add auto constraints at the end of the second side
                if (sugConstr2.size() > 0) {
                    createAutoConstraints(sugConstr2, getHighestCurveIndex() - 3, Sketcher::PointPos::end);
                    sugConstr2.clear();
                }
            }

            tryAutoRecomputeIfNotSolve(static_cast<Sketcher::SketchObject *>(sketchgui->getObject()));

            ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Mod/Sketcher");
            bool continuousMode = hGrp->GetBool("ContinuousCreationMode",true);
            if(continuousMode){
            // This code enables the continuous creation mode.
                Mode=STATUS_SEEK_First;
                EditCurve.clear();
                drawEdit(EditCurve);
                EditCurve.resize(5);
                applyCursor();
                /* this is ok not to call to purgeHandler
                * in continuous creation mode because the
                * handler is destroyed by the quit() method on pressing the
                * right button of the mouse */
            }
            else{
                sketchgui->purgeHandler(); // no code after this line, Handler get deleted in ViewProvider
            }


        }
        return true;
    }

private:

    virtual void activated() override
    {
        setCrosshairCursor("Sketcher_Pointer_Create_Box");
    }
protected:
    BoxMode Mode;
    std::vector<Base::Vector2d> EditCurve;
    std::vector<AutoConstraint> sugConstr1, sugConstr2;
    ConstructionMethod constructionMethod;
    Base::Vector2d center;
};

DEF_STD_CMD_AU(CmdSketcherCreateRectangle)

CmdSketcherCreateRectangle::CmdSketcherCreateRectangle()
  : Command("Sketcher_CreateRectangle")
{
    sAppModule      = "Sketcher";
    sGroup          = "Sketcher";
    sMenuText       = QT_TR_NOOP("Create rectangle");
    sToolTipText    = QT_TR_NOOP("Create a rectangle in the sketch");
    sWhatsThis      = "Sketcher_CreateRectangle";
    sStatusTip      = sToolTipText;
    sPixmap         = "Sketcher_CreateRectangle";
    sAccel          = "G, R";
    eType           = ForEdit;
}

CONSTRUCTION_UPDATE_ACTION(CmdSketcherCreateRectangle, "Sketcher_CreateRectangle")

void CmdSketcherCreateRectangle::activated(int iMsg)
{
    Q_UNUSED(iMsg);
    ActivateHandler(getActiveGuiDocument(),new DrawSketchHandlerBox(DrawSketchHandlerBox::Diagonal) );
}

bool CmdSketcherCreateRectangle::isActive(void)
{
    return isCreateGeoActive(getActiveGuiDocument());
}

DEF_STD_CMD_AU(CmdSketcherCreateRectangleCenter)

CmdSketcherCreateRectangleCenter::CmdSketcherCreateRectangleCenter()
  : Command("Sketcher_CreateRectangle_Center")
{
    sAppModule      = "Sketcher";
    sGroup          = "Sketcher";
    sMenuText       = QT_TR_NOOP("Create centered rectangle");
    sToolTipText    = QT_TR_NOOP("Create a centered rectangle in the sketch");
    sWhatsThis      = "Sketcher_CreateRectangle_Center";
    sStatusTip      = sToolTipText;
    sPixmap         = "Sketcher_CreateRectangle_Center";
    sAccel          = "G, V";
    eType           = ForEdit;
}

CONSTRUCTION_UPDATE_ACTION(CmdSketcherCreateRectangleCenter, "Sketcher_CreateRectangle_Center")

void CmdSketcherCreateRectangleCenter::activated(int iMsg)
{
    Q_UNUSED(iMsg);
    ActivateHandler(getActiveGuiDocument(),new DrawSketchHandlerBox(DrawSketchHandlerBox::CenterAndCorner) );
}

bool CmdSketcherCreateRectangleCenter::isActive(void)
{
    return isCreateGeoActive(getActiveGuiDocument());
}


/* Create rounded oblong =======================================================*/

class DrawSketchHandlerOblong : public DrawSketchHandler
{
public:
    DrawSketchHandlerOblong()
        : Mode(STATUS_SEEK_First)
        , lengthX(0), lengthY(0), radius(0), signX(1), signY(1)
        , EditCurve(37)
    {
    }
    virtual ~DrawSketchHandlerOblong() {}
    /// mode table
    enum BoxMode {
        STATUS_SEEK_First,      /**< enum value ----. */
        STATUS_SEEK_Second,     /**< enum value ----. */
        STATUS_End
    };

    virtual void mouseMove(Base::Vector2d onSketchPos) override
    {

        if (Mode == STATUS_SEEK_First) {
            setPositionText(onSketchPos);
            if (seekAutoConstraint(sugConstr1, onSketchPos, Base::Vector2d(0.f, 0.f))) {
                renderSuggestConstraintsCursor(sugConstr1);
                return;
            }
        }
        else if (Mode == STATUS_SEEK_Second) {
            float distanceX = onSketchPos.x - StartPos.x;
            float distanceY = onSketchPos.y - StartPos.y;

            lengthX = distanceX; lengthY = distanceY;
            signX = Base::sgn(distanceX);
            signY = Base::sgn(distanceY);
            if (fabs(distanceX) > fabs(distanceY)) {
                radius = fabs(distanceY) / 4; // we use a fourth of the smaller distance as default radius
            }
            else {
                radius = fabs(distanceX) / 4;
            }

            // we draw the lines with 36 segments, 8 for each arc and 4 lines
            // draw the arcs
            for (int i = 0; i < 8; i++) {
                // calculate the x,y positions forming the the arc
                double angle = i * M_PI / 16.0;
                double x_i = -radius * sin(angle);
                double y_i = -radius * cos(angle);
                // we are drawing clockwise starting with the arc that is besides StartPos
                if (signX == signY) {
                    EditCurve[i] = Base::Vector2d(StartPos.x + signX * (radius + x_i), StartPos.y + signY * (radius + y_i));
                    EditCurve[9 + i] = Base::Vector2d(StartPos.x + signY * (radius + y_i), StartPos.y + lengthY - signX * (radius + x_i));
                    EditCurve[18 + i] = Base::Vector2d(StartPos.x + lengthX - signX * (radius + x_i), StartPos.y + lengthY - signY * (radius + y_i));
                    EditCurve[27 + i] = Base::Vector2d(StartPos.x + lengthX - signY * (radius + y_i), StartPos.y + signX * (radius + x_i));
                }
                else {
                    EditCurve[i] = Base::Vector2d(StartPos.x - signY * (radius + y_i), StartPos.y - signX * (radius + x_i));
                    EditCurve[9 + i] = Base::Vector2d(StartPos.x + lengthX - signX * (radius + x_i), StartPos.y + signY * (radius + y_i));
                    EditCurve[18 + i] = Base::Vector2d(StartPos.x + lengthX + signY * (radius + y_i), StartPos.y + lengthY + signX * (radius + x_i));
                    EditCurve[27 + i] = Base::Vector2d(StartPos.x + signX * (radius + x_i), StartPos.y + lengthY - signY * (radius + y_i));
                }
            }
            // draw the lines
            if (signX == signY) {
                EditCurve[8] = Base::Vector2d(StartPos.x, StartPos.y + (signY * radius));
                EditCurve[17] = Base::Vector2d(StartPos.x + (signX * radius), StartPos.y + lengthY);
                EditCurve[26] = Base::Vector2d(StartPos.x + lengthX, StartPos.y + lengthY - (signY * radius));
                EditCurve[35] = Base::Vector2d(StartPos.x + lengthX - (signX * radius), StartPos.y);
            }
            else {
                EditCurve[8] = Base::Vector2d(StartPos.x + (signX * radius), StartPos.y);
                EditCurve[17] = Base::Vector2d(StartPos.x + lengthX, StartPos.y + (signY * radius));
                EditCurve[26] = Base::Vector2d(StartPos.x + lengthX - (signX * radius), StartPos.y + lengthY);
                EditCurve[35] = Base::Vector2d(StartPos.x, StartPos.y + lengthY - (signY * radius));
            }
            // close the curve
            EditCurve[36] = EditCurve[0];

            SbString text;
            text.sprintf(" (%.1fR %.1fX %.1fY)", radius, lengthX, lengthY);
            setPositionText(onSketchPos, text);

            drawEdit(EditCurve);
            if (seekAutoConstraint(sugConstr2, onSketchPos, Base::Vector2d(0.f, 0.f))) {
                renderSuggestConstraintsCursor(sugConstr2);
                return;
            }
        }
        applyCursor();
    }

    virtual bool pressButton(Base::Vector2d onSketchPos) override
    {
        if (Mode == STATUS_SEEK_First) {
            StartPos = onSketchPos;
            Mode = STATUS_SEEK_Second;
        }
        else {
            EndPos = onSketchPos;
            Mode = STATUS_End;
        }
        return true;
    }

    virtual bool releaseButton(Base::Vector2d onSketchPos) override
    {
        Q_UNUSED(onSketchPos);
        if (Mode == STATUS_End) {
            unsetCursor();
            resetPositionText();

            int firstCurve = getHighestCurveIndex() + 1;
            // add the geometry to the sketch
            // first determine the angles for the first arc
            double start = 0;
            double end = M_PI / 2;
            if (signX > 0 && signY > 0) {
                start = -2 * end;
                end = -1 * end;
            }
            else if (signX > 0 && signY < 0) {
                start = end;
                end = 2 * end;
            }
            else if (signX < 0 && signY > 0) {
                start = -1 * end;
                end = 0;
            }

            try {
                Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Add rounded rectangle"));
                Gui::Command::doCommand(Gui::Command::Doc,
                    // syntax for arcs: Part.ArcOfCircle(Part.Circle(center, axis, radius), startangle, endangle)
                    "geoList = []\n"
                    "geoList.append(Part.ArcOfCircle(Part.Circle(App.Vector(%f, %f, 0), App.Vector(0, 0, 1), %f), %f, %f))\n"
                    "geoList.append(Part.LineSegment(App.Vector(%f, %f, 0), App.Vector(%f, %f, 0)))\n"
                    "geoList.append(Part.ArcOfCircle(Part.Circle(App.Vector(%f, %f, 0), App.Vector(0, 0, 1), %f), %f, %f))\n"
                    "geoList.append(Part.LineSegment(App.Vector(%f, %f, 0), App.Vector(%f, %f, 0)))\n"
                    "geoList.append(Part.ArcOfCircle(Part.Circle(App.Vector(%f, %f, 0), App.Vector(0, 0, 1), %f), %f, %f))\n"
                    "geoList.append(Part.LineSegment(App.Vector(%f, %f, 0), App.Vector(%f, %f, 0)))\n"
                    "geoList.append(Part.ArcOfCircle(Part.Circle(App.Vector(%f, %f, 0), App.Vector(0, 0, 1), %f), %f, %f))\n"
                    "geoList.append(Part.LineSegment(App.Vector(%f, %f, 0), App.Vector(%f, %f, 0)))\n"
                    "%s.addGeometry(geoList, %s)\n"
                    "conList = []\n"
                    "conList.append(Sketcher.Constraint('Tangent', %i, 1, %i, 1))\n"
                    "conList.append(Sketcher.Constraint('Tangent', %i, 2, %i, 2))\n"
                    "conList.append(Sketcher.Constraint('Tangent', %i, 1, %i, 1))\n"
                    "conList.append(Sketcher.Constraint('Tangent', %i, 2, %i, 2))\n"
                    "conList.append(Sketcher.Constraint('Tangent', %i, 1, %i, 1))\n"
                    "conList.append(Sketcher.Constraint('Tangent', %i, 2, %i, 2))\n"
                    "conList.append(Sketcher.Constraint('Tangent', %i, 1, %i, 1))\n"
                    "conList.append(Sketcher.Constraint('Tangent', %i, 2, %i, 2))\n"
                    "conList.append(Sketcher.Constraint('Horizontal', %i))\n"
                    "conList.append(Sketcher.Constraint('Horizontal', %i))\n"
                    "conList.append(Sketcher.Constraint('Vertical', %i))\n"
                    "conList.append(Sketcher.Constraint('Vertical', %i))\n"
                    "conList.append(Sketcher.Constraint('Equal', %i, %i))\n"
                    "conList.append(Sketcher.Constraint('Equal', %i, %i))\n"
                    "conList.append(Sketcher.Constraint('Equal', %i, %i))\n"
                    "%s.addConstraint(conList)\n"
                    "del geoList, conList\n",
                    StartPos.x + (signX * radius), StartPos.y + (signY * radius), // center of the  arc 1
                    radius,
                    start, end,                 // start and end angle of arc1
                    EditCurve[8].x, EditCurve[8].y, EditCurve[9].x, EditCurve[9].y, // line 1
                    signX == signY ? StartPos.x + (signX * radius) : StartPos.x + lengthX - (signX * radius), // center of the arc 2
                    signX == signY ? StartPos.y + lengthY - (signY * radius) : StartPos.y + (signY * radius),
                    radius,
                    // start and end angle of arc 2
                    // the logic is that end is start + M_PI / 2 and start is the previous end - M_PI
                    end - M_PI,
                    end - 0.5 * M_PI,
                    EditCurve[17].x, EditCurve[17].y, EditCurve[18].x, EditCurve[18].y, // line 2
                    StartPos.x + lengthX - (signX * radius), StartPos.y + lengthY - (signY * radius),  // center of the arc 3
                    radius,
                    end - 1.5 * M_PI,
                    end - M_PI,
                    EditCurve[26].x, EditCurve[26].y, EditCurve[27].x, EditCurve[27].y, // line 3
                    signX == signY ? StartPos.x + lengthX - (signX * radius) : StartPos.x + (signX * radius), // center of the arc 4
                    signX == signY ? StartPos.y + (signY * radius) : StartPos.y + lengthY - (signY * radius),
                    radius,
                    end - 2 * M_PI,
                    end - 1.5 * M_PI,
                    EditCurve[35].x, EditCurve[35].y, EditCurve[36].x, EditCurve[36].y, // line 4
                    Gui::Command::getObjectCmd(sketchgui->getObject()).c_str(), // the sketch
                    geometryCreationMode == Construction ? "True" : "False", // geometry as construction or not
                    firstCurve, firstCurve + 1,     // tangent 1
                    firstCurve + 1, firstCurve + 2, // tangent 2
                    firstCurve + 2, firstCurve + 3, // tangent 3
                    firstCurve + 3, firstCurve + 4, // tangent 4
                    firstCurve + 4, firstCurve + 5, // tangent 5
                    firstCurve + 5, firstCurve + 6, // tangent 6
                    firstCurve + 6, firstCurve + 7, // tangent 7
                    firstCurve + 7, firstCurve,     // tangent 8
                    signX == signY ? firstCurve + 3 : firstCurve + 1, // horizontal constraint
                    signX == signY ? firstCurve + 7 : firstCurve + 5, // horizontal constraint
                    signX == signY ? firstCurve + 1 : firstCurve + 3, // vertical constraint
                    signX == signY ? firstCurve + 5 : firstCurve + 7, // vertical constraint
                    firstCurve, firstCurve + 2,     // equal  1
                    firstCurve + 2, firstCurve + 4, // equal  2
                    firstCurve + 4, firstCurve + 6, // equal  3
                    Gui::Command::getObjectCmd(sketchgui->getObject()).c_str()); // the sketch

                // now add construction geometry - two points used to take suggested constraints
                Gui::Command::doCommand(Gui::Command::Doc,
                    "geoList = []\n"
                    "geoList.append(Part.Point(App.Vector(%f, %f, 0)))\n"
                    "geoList.append(Part.Point(App.Vector(%f, %f, 0)))\n"
                    "%s.addGeometry(geoList, True)\n" // geometry as construction
                    "conList = []\n"
                    "conList.append(Sketcher.Constraint('PointOnObject', %i, 1, %i, ))\n"
                    "conList.append(Sketcher.Constraint('PointOnObject', %i, 1, %i, ))\n"
                    "conList.append(Sketcher.Constraint('PointOnObject', %i, 1, %i, ))\n"
                    "conList.append(Sketcher.Constraint('PointOnObject', %i, 1, %i, ))\n"
                    "%s.addConstraint(conList)\n"
                    "del geoList, conList\n",
                    StartPos.x, StartPos.y, // point at StartPos
                    EndPos.x, EndPos.y,     // point at EndPos
                    Gui::Command::getObjectCmd(sketchgui->getObject()).c_str(), // the sketch
                    firstCurve + 8, firstCurve + 1, // point on object constraint
                    firstCurve + 8, firstCurve + 7, // point on object constraint
                    firstCurve + 9, firstCurve + 3, // point on object constraint
                    firstCurve + 9, firstCurve + 5, // point on object constraint
                    Gui::Command::getObjectCmd(sketchgui->getObject()).c_str()); // the sketch

                Gui::Command::commitCommand();

                // add auto constraints at the StartPos auxiliary point
                if (sugConstr1.size() > 0) {
                    createAutoConstraints(sugConstr1, getHighestCurveIndex() - 1, Sketcher::PointPos::start);
                    sugConstr1.clear();
                }

                // add auto constraints at the EndPos auxiliary point
                if (sugConstr2.size() > 0) {
                    createAutoConstraints(sugConstr2, getHighestCurveIndex(), Sketcher::PointPos::start);
                    sugConstr2.clear();
                }

                tryAutoRecomputeIfNotSolve(static_cast<Sketcher::SketchObject*>(sketchgui->getObject()));
            }
            catch (const Base::Exception& e) {
                Base::Console().Error("Failed to add rounded rectangle: %s\n", e.what());
                Gui::Command::abortCommand();

                tryAutoRecompute(static_cast<Sketcher::SketchObject*>(sketchgui->getObject()));
            }
            ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Mod/Sketcher");
            bool continuousMode = hGrp->GetBool("ContinuousCreationMode", true);

            if (continuousMode) {
                // This code enables the continuous creation mode.
                Mode = STATUS_SEEK_First;
                EditCurve.clear();
                drawEdit(EditCurve);
                EditCurve.resize(37);
                applyCursor();
                /* this is ok not to call to purgeHandler
                * in continuous creation mode because the
                * handler is destroyed by the quit() method on pressing the
                * right button of the mouse */
            }
            else {
                sketchgui->purgeHandler(); // no code after this line, Handler get deleted in ViewProvider
            }
        }
        return true;
    }

private:
    virtual void activated() override
    {
        setCrosshairCursor("Sketcher_Pointer_Oblong");
    }

protected:
    BoxMode Mode;
    Base::Vector2d StartPos, EndPos;
    double lengthX, lengthY, radius;
    float signX, signY;
    std::vector<Base::Vector2d> EditCurve;
    std::vector<AutoConstraint> sugConstr1, sugConstr2;
};

DEF_STD_CMD_AU(CmdSketcherCreateOblong)

CmdSketcherCreateOblong::CmdSketcherCreateOblong()
    : Command("Sketcher_CreateOblong")
{
    sAppModule = "Sketcher";
    sGroup = "Sketcher";
    sMenuText = QT_TR_NOOP("Create rounded rectangle");
    sToolTipText = QT_TR_NOOP("Create a rounded rectangle in the sketch");
    sWhatsThis = "Sketcher_CreateOblong";
    sStatusTip = sToolTipText;
    sPixmap = "Sketcher_CreateOblong";
    sAccel = "G, O";
    eType = ForEdit;
}

CONSTRUCTION_UPDATE_ACTION(CmdSketcherCreateOblong, "Sketcher_CreateOblong")

void CmdSketcherCreateOblong::activated(int iMsg)
{
    Q_UNUSED(iMsg);
    ActivateHandler(getActiveGuiDocument(), new DrawSketchHandlerOblong());
}

bool CmdSketcherCreateOblong::isActive(void)
{
    return isCreateGeoActive(getActiveGuiDocument());
}

/* Rectangles Comp command =========================================*/

DEF_STD_CMD_ACLU(CmdSketcherCompCreateRectangles)

CmdSketcherCompCreateRectangles::CmdSketcherCompCreateRectangles()
    : Command("Sketcher_CompCreateRectangles")
{
    sAppModule = "Sketcher";
    sGroup = "Sketcher";
    sMenuText = QT_TR_NOOP("Create rectangles");
    sToolTipText = QT_TR_NOOP("Creates a rectangle in the sketch");
    sWhatsThis = "Sketcher_CompCreateRectangles";
    sStatusTip = sToolTipText;
    eType = ForEdit;
}

void CmdSketcherCompCreateRectangles::activated(int iMsg)
{
    if (iMsg == 0)
        ActivateHandler(getActiveGuiDocument(), new DrawSketchHandlerBox(DrawSketchHandlerBox::Diagonal));
    else if (iMsg == 1)
        ActivateHandler(getActiveGuiDocument(), new DrawSketchHandlerBox(DrawSketchHandlerBox::CenterAndCorner));
    else if (iMsg == 2)
        ActivateHandler(getActiveGuiDocument(), new DrawSketchHandlerOblong());
    else
        return;

    // Since the default icon is reset when enabling/disabling the command we have
    // to explicitly set the icon of the used command.
    // dynamic_cast<>
    Gui::ActionGroup* pcAction = dynamic_cast<Gui::ActionGroup*>(_pcAction);
    QList<QAction*> a = pcAction->actions();

    assert(iMsg < a.size());
    pcAction->setIcon(a[iMsg]->icon());
}

Gui::Action* CmdSketcherCompCreateRectangles::createAction(void)
{
    Gui::ActionGroup* pcAction = new Gui::ActionGroup(this, Gui::getMainWindow());
    pcAction->setDropDownMenu(true);
    applyCommandData(this->className(), pcAction);

    QAction* arc1 = pcAction->addAction(QString());
    arc1->setIcon(Gui::BitmapFactory().iconFromTheme("Sketcher_CreateRectangle"));
    QAction* arc2 = pcAction->addAction(QString());
    arc2->setIcon(Gui::BitmapFactory().iconFromTheme("Sketcher_CreateRectangle_Center"));
    QAction* arc3 = pcAction->addAction(QString());
    arc3->setIcon(Gui::BitmapFactory().iconFromTheme("Sketcher_CreateOblong"));

    _pcAction = pcAction;
    languageChange();

    pcAction->setIcon(arc1->icon());
    int defaultId = 0;
    pcAction->setProperty("defaultAction", QVariant(defaultId));

    return pcAction;
}

void CmdSketcherCompCreateRectangles::updateAction(int mode)
{
    Gui::ActionGroup* pcAction = dynamic_cast<Gui::ActionGroup*>(getAction());
    if (!pcAction)
        return;

    QList<QAction*> a = pcAction->actions();
    int index =0;// = pcAction->property("defaultAction").toInt();
    switch (mode) {
    case Normal:
        a[0]->setIcon(Gui::BitmapFactory().iconFromTheme("Sketcher_CreateRectangle"));
        a[1]->setIcon(Gui::BitmapFactory().iconFromTheme("Sketcher_CreateRectangle_Center"));
        a[2]->setIcon(Gui::BitmapFactory().iconFromTheme("Sketcher_CreateOblong"));
        getAction()->setIcon(a[index]->icon());
        break;
    case Construction:
        a[0]->setIcon(Gui::BitmapFactory().iconFromTheme("Sketcher_CreateRectangle_Constr"));
        a[1]->setIcon(Gui::BitmapFactory().iconFromTheme("Sketcher_CreateRectangle_Center_Constr"));
        a[2]->setIcon(Gui::BitmapFactory().iconFromTheme("Sketcher_CreateOblong_Constr"));
        getAction()->setIcon(a[index]->icon());
        break;
    }
}

void CmdSketcherCompCreateRectangles::languageChange()
{
    Command::languageChange();

    if (!_pcAction)
        return;
    Gui::ActionGroup* pcAction = dynamic_cast<Gui::ActionGroup*>(_pcAction);
    QList<QAction*> a = pcAction->actions();

    QAction* rectangle1 = a[0];
    rectangle1->setText(QApplication::translate("CmdSketcherCompCreateRectangles", "Rectangle"));
    rectangle1->setToolTip(QApplication::translate("Sketcher_CreateRectangle", "Create a rectangle"));
    rectangle1->setStatusTip(rectangle1->toolTip());
    QAction* rectangle2 = a[1];
    rectangle2->setText(QApplication::translate("CmdSketcherCompCreateRectangles", "Centered rectangle"));
    rectangle2->setToolTip(QApplication::translate("Sketcher_CreateRectangle_Center", "Create a centered rectangle"));
    rectangle2->setStatusTip(rectangle2->toolTip());
    QAction* rectangle3 = a[2];
    rectangle3->setText(QApplication::translate("CmdSketcherCompCreateRectangles", "Rounded rectangle"));
    rectangle3->setToolTip(QApplication::translate("Sketcher_CreateOblong", "Create a rounded rectangle"));
    rectangle3->setStatusTip(rectangle3->toolTip());
}

bool CmdSketcherCompCreateRectangles::isActive(void)
{
    return isCreateGeoActive(getActiveGuiDocument());
}

// ======================================================================================

class DrawSketchHandlerLineSet: public DrawSketchHandler
{
public:
    DrawSketchHandlerLineSet()
      : Mode(STATUS_SEEK_First), SegmentMode(SEGMENT_MODE_Line)
      , TransitionMode(TRANSITION_MODE_Free)
      , SnapMode(SNAP_MODE_Free)
      , suppressTransition(false)
      , EditCurve(2)
      , firstCurve(-1)
      , previousCurve(-1)
      , firstPosId(Sketcher::PointPos::none)
      , previousPosId(Sketcher::PointPos::none)
      , startAngle(0)
      , endAngle(0)
      , arcRadius(0)
      , firstsegment(true)
    {
    }
    virtual ~DrawSketchHandlerLineSet() {}
    /// mode table
    enum SELECT_MODE {
        STATUS_SEEK_First,      /**< enum value ----. */
        STATUS_SEEK_Second,     /**< enum value ----. */
        STATUS_Do,
        STATUS_Close
    };

    enum SEGMENT_MODE
    {
        SEGMENT_MODE_Arc,
        SEGMENT_MODE_Line
    };

    enum TRANSITION_MODE
    {
        TRANSITION_MODE_Free,
        TRANSITION_MODE_Tangent,
        TRANSITION_MODE_Perpendicular_L,
        TRANSITION_MODE_Perpendicular_R
    };

    enum SNAP_MODE
    {
        SNAP_MODE_Free,
        SNAP_MODE_45Degree
    };

    virtual void registerPressedKey(bool pressed, int key) override
    {
        if (Mode != STATUS_SEEK_Second)
            return; // SegmentMode can be changed only in STATUS_SEEK_Second mode

        if (key == SoKeyboardEvent::M && pressed && previousCurve != -1) {
            // loop through the following modes:
            // SEGMENT_MODE_Line, TRANSITION_MODE_Free / TRANSITION_MODE_Tangent
            // SEGMENT_MODE_Line, TRANSITION_MODE_Perpendicular_L
            // SEGMENT_MODE_Line, TRANSITION_MODE_Tangent / TRANSITION_MODE_Free
            // SEGMENT_MODE_Arc, TRANSITION_MODE_Tangent
            // SEGMENT_MODE_Arc, TRANSITION_MODE_Perpendicular_L
            // SEGMENT_MODE_Arc, TRANSITION_MODE_Perpendicular_R

            SnapMode = SNAP_MODE_Free;

            Base::Vector2d onSketchPos;
            if (SegmentMode == SEGMENT_MODE_Line)
                onSketchPos = EditCurve[EditCurve.size()-1];
            else
                onSketchPos = EditCurve[29];

            const Part::Geometry *geom = sketchgui->getSketchObject()->getGeometry(previousCurve);

            if (SegmentMode == SEGMENT_MODE_Line) {
                switch (TransitionMode) {
                    case TRANSITION_MODE_Free:
                        if (geom->getTypeId() == Part::GeomArcOfCircle::getClassTypeId()) { // 3rd mode
                            SegmentMode = SEGMENT_MODE_Arc;
                            TransitionMode = TRANSITION_MODE_Tangent;
                        }
                        else // 1st mode
                            TransitionMode = TRANSITION_MODE_Perpendicular_L;
                        break;
                    case TRANSITION_MODE_Perpendicular_L: // 2nd mode
                        if (geom->getTypeId() == Part::GeomArcOfCircle::getClassTypeId())
                            TransitionMode = TRANSITION_MODE_Free;
                        else
                            TransitionMode = TRANSITION_MODE_Tangent;
                        break;
                    case TRANSITION_MODE_Tangent:
                        if (geom->getTypeId() == Part::GeomArcOfCircle::getClassTypeId()) // 1st mode
                            TransitionMode = TRANSITION_MODE_Perpendicular_L;
                        else { // 3rd mode
                            SegmentMode = SEGMENT_MODE_Arc;
                            TransitionMode = TRANSITION_MODE_Tangent;
                        }
                        break;
                    default: // unexpected mode
                        TransitionMode = TRANSITION_MODE_Free;
                        break;
                }
            }
            else {
                switch (TransitionMode) {
                    case TRANSITION_MODE_Tangent: // 4th mode
                        TransitionMode = TRANSITION_MODE_Perpendicular_L;
                        break;
                    case TRANSITION_MODE_Perpendicular_L: // 5th mode
                        TransitionMode = TRANSITION_MODE_Perpendicular_R;
                        break;
                    default: // 6th mode (Perpendicular_R) + unexpected mode
                        SegmentMode = SEGMENT_MODE_Line;
                        if (geom->getTypeId() == Part::GeomArcOfCircle::getClassTypeId())
                            TransitionMode = TRANSITION_MODE_Tangent;
                        else
                            TransitionMode = TRANSITION_MODE_Free;
                        break;
                }
            }

            if (SegmentMode == SEGMENT_MODE_Line)
                EditCurve.resize(TransitionMode == TRANSITION_MODE_Free ? 2 : 3);
            else
                EditCurve.resize(32);
            mouseMove(onSketchPos); // trigger an update of EditCurve
        }
    }

    virtual void mouseMove(Base::Vector2d onSketchPos) override
    {
        suppressTransition = false;
        if (Mode==STATUS_SEEK_First) {
            setPositionText(onSketchPos);
            if (seekAutoConstraint(sugConstr1, onSketchPos, Base::Vector2d(0.f,0.f))) {
                renderSuggestConstraintsCursor(sugConstr1);
                return;
            }
        }
        else if (Mode==STATUS_SEEK_Second){
            if (SegmentMode == SEGMENT_MODE_Line) {
                EditCurve[EditCurve.size()-1] = onSketchPos;
                if (TransitionMode == TRANSITION_MODE_Tangent) {
                    Base::Vector2d Tangent(dirVec.x,dirVec.y);
                    EditCurve[1].ProjectToLine(EditCurve[2] - EditCurve[0], Tangent);
                    if (EditCurve[1] * Tangent < 0) {
                        EditCurve[1] = EditCurve[2];
                        suppressTransition = true;
                    }
                    else
                        EditCurve[1] = EditCurve[0] + EditCurve[1];
                }
                else if (TransitionMode == TRANSITION_MODE_Perpendicular_L ||
                         TransitionMode == TRANSITION_MODE_Perpendicular_R) {
                    Base::Vector2d Perpendicular(-dirVec.y,dirVec.x);
                    EditCurve[1].ProjectToLine(EditCurve[2] - EditCurve[0], Perpendicular);
                    EditCurve[1] = EditCurve[0] + EditCurve[1];
                }

                drawEdit(EditCurve);

                float length = (EditCurve[1] - EditCurve[0]).Length();
                float angle = (EditCurve[1] - EditCurve[0]).GetAngle(Base::Vector2d(1.f,0.f));

                SbString text;
                text.sprintf(" (%.1f,%.1fdeg)", length, angle * 180 / M_PI);
                setPositionText(EditCurve[1], text);

                if (TransitionMode == TRANSITION_MODE_Free) {
                    if (seekAutoConstraint(sugConstr2, onSketchPos, onSketchPos - EditCurve[0])) {
                        renderSuggestConstraintsCursor(sugConstr2);
                        return;
                    }
                }
            }
            else if (SegmentMode == SEGMENT_MODE_Arc) {

                if(QApplication::keyboardModifiers() == Qt::ControlModifier)
                    SnapMode = SNAP_MODE_45Degree;
                else
                    SnapMode = SNAP_MODE_Free;

                Base::Vector2d Tangent;
                if  (TransitionMode == TRANSITION_MODE_Tangent)
                    Tangent = Base::Vector2d(dirVec.x,dirVec.y);
                else if  (TransitionMode == TRANSITION_MODE_Perpendicular_L)
                    Tangent = Base::Vector2d(-dirVec.y,dirVec.x);
                else if  (TransitionMode == TRANSITION_MODE_Perpendicular_R)
                    Tangent = Base::Vector2d(dirVec.y,-dirVec.x);

                double theta = Tangent.GetAngle(onSketchPos - EditCurve[0]);

                arcRadius = (onSketchPos - EditCurve[0]).Length()/(2.0*sin(theta));

                // At this point we need a unit normal vector pointing towards
                // the center of the arc we are drawing. Derivation of the formula
                // used here can be found at http://people.richland.edu/james/lecture/m116/matrices/area.html
                double x1 = EditCurve[0].x;
                double y1 = EditCurve[0].y;
                double x2 = x1 + Tangent.x;
                double y2 = y1 + Tangent.y;
                double x3 = onSketchPos.x;
                double y3 = onSketchPos.y;
                if ((x2*y3-x3*y2)-(x1*y3-x3*y1)+(x1*y2-x2*y1) > 0)
                    arcRadius *= -1;
                if (boost::math::isnan(arcRadius) || boost::math::isinf(arcRadius))
                    arcRadius = 0.f;

                CenterPoint = EditCurve[0] + Base::Vector2d(arcRadius * Tangent.y, -arcRadius * Tangent.x);

                double rx = EditCurve[0].x - CenterPoint.x;
                double ry = EditCurve[0].y - CenterPoint.y;

                startAngle = atan2(ry,rx);

                double rxe = onSketchPos.x - CenterPoint.x;
                double rye = onSketchPos.y - CenterPoint.y;
                double arcAngle = atan2(-rxe*ry + rye*rx, rxe*rx + rye*ry);
                if (boost::math::isnan(arcAngle) || boost::math::isinf(arcAngle))
                    arcAngle = 0.f;
                if (arcRadius >= 0 && arcAngle > 0)
                    arcAngle -=  2*M_PI;
                if (arcRadius < 0 && arcAngle < 0)
                    arcAngle +=  2*M_PI;

                if (SnapMode == SNAP_MODE_45Degree)
                    arcAngle = round(arcAngle / (M_PI/4)) * M_PI/4;

                endAngle = startAngle + arcAngle;

                for (int i=1; i <= 29; i++) {
                    double angle = i*arcAngle/29.0;
                    double dx = rx * cos(angle) - ry * sin(angle);
                    double dy = rx * sin(angle) + ry * cos(angle);
                    EditCurve[i] = Base::Vector2d(CenterPoint.x + dx, CenterPoint.y + dy);
                }

                EditCurve[30] = CenterPoint;
                EditCurve[31] = EditCurve[0];

                drawEdit(EditCurve);

                SbString text;
                text.sprintf(" (%.1fR,%.1fdeg)", std::abs(arcRadius), arcAngle * 180 / M_PI);
                setPositionText(onSketchPos, text);

                if (seekAutoConstraint(sugConstr2, onSketchPos, Base::Vector2d(0.f,0.f))) {
                    renderSuggestConstraintsCursor(sugConstr2);
                    return;
                }
            }
        }
        applyCursor();
    }

    virtual bool pressButton(Base::Vector2d onSketchPos) override
    {
        if (Mode == STATUS_SEEK_First) {

            EditCurve[0] = onSketchPos; // this may be overwritten if previousCurve is found

            virtualsugConstr1 = sugConstr1; // store original autoconstraints.

            // here we check if there is a preselected point and
            // we set up a transition from the neighbouring segment.
            // (peviousCurve, previousPosId, dirVec, TransitionMode)
            for (unsigned int i=0; i < sugConstr1.size(); i++)
                if (sugConstr1[i].Type == Sketcher::Coincident) {
                    const Part::Geometry *geom = sketchgui->getSketchObject()->getGeometry(sugConstr1[i].GeoId);
                    if ((geom->getTypeId() == Part::GeomLineSegment::getClassTypeId() ||
                         geom->getTypeId() == Part::GeomArcOfCircle::getClassTypeId()) &&
                        (sugConstr1[i].PosId == Sketcher::PointPos::start ||
                         sugConstr1[i].PosId == Sketcher::PointPos::end)) {
                        previousCurve = sugConstr1[i].GeoId;
                        previousPosId = sugConstr1[i].PosId;
                        updateTransitionData(previousCurve,previousPosId); // -> dirVec, EditCurve[0]
                        if (geom->getTypeId() == Part::GeomArcOfCircle::getClassTypeId()) {
                            TransitionMode = TRANSITION_MODE_Tangent;
                            SnapMode = SNAP_MODE_Free;
                        }
                        sugConstr1.erase(sugConstr1.begin()+i); // actually we should clear the vector completely
                        break;
                    }
                }

            // remember our first point (even if we are doing a transition from a previous curve)
            firstCurve = getHighestCurveIndex() + 1;
            firstPosId = Sketcher::PointPos::start;

            if (SegmentMode == SEGMENT_MODE_Line)
                EditCurve.resize(TransitionMode == TRANSITION_MODE_Free ? 2 : 3);
            else if (SegmentMode == SEGMENT_MODE_Arc)
                EditCurve.resize(32);
            Mode = STATUS_SEEK_Second;
        }
        else if (Mode == STATUS_SEEK_Second) {
            // exit on clicking exactly at the same position (e.g. double click)
            if (onSketchPos == EditCurve[0]) {
                unsetCursor();
                resetPositionText();
                EditCurve.clear();
                drawEdit(EditCurve);

                ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Mod/Sketcher");
                bool continuousMode = hGrp->GetBool("ContinuousCreationMode",true);

                if(continuousMode){
                    // This code enables the continuous creation mode.
                    Mode=STATUS_SEEK_First;
                    SegmentMode=SEGMENT_MODE_Line;
                    TransitionMode=TRANSITION_MODE_Free;
                    SnapMode = SNAP_MODE_Free;
                    suppressTransition=false;
                    firstCurve=-1;
                    previousCurve=-1;
                    firstPosId=Sketcher::PointPos::none;
                    previousPosId=Sketcher::PointPos::none;
                    EditCurve.clear();
                    drawEdit(EditCurve);
                    EditCurve.resize(2);
                    applyCursor();
                    /* this is ok not to call to purgeHandler
                    * in continuous creation mode because the
                    * handler is destroyed by the quit() method on pressing the
                    * right button of the mouse */
                    return true;
                }
                else{
                    sketchgui->purgeHandler(); // no code after this line, Handler get deleted in ViewProvider
                    return true;
                }
            }

            Mode = STATUS_Do;

            if (getPreselectPoint() != -1 && firstPosId != Sketcher::PointPos::none) {
                int GeoId;
                Sketcher::PointPos PosId;
                sketchgui->getSketchObject()->getGeoVertexIndex(getPreselectPoint(),GeoId,PosId);
                if (sketchgui->getSketchObject()->arePointsCoincident(GeoId,PosId,firstCurve,firstPosId))
                    Mode = STATUS_Close;
            }
            else if (getPreselectCross() == 0 && firstPosId != Sketcher::PointPos::none) {
                // close line started at root point
                if (sketchgui->getSketchObject()->arePointsCoincident(-1,Sketcher::PointPos::start,firstCurve,firstPosId))
                    Mode = STATUS_Close;
            }
        }
        return true;
    }

    virtual bool releaseButton(Base::Vector2d onSketchPos) override
    {
        if (Mode == STATUS_Do || Mode == STATUS_Close) {
            bool addedGeometry = true;
            if (SegmentMode == SEGMENT_MODE_Line) {
                // issue the geometry
                try {
                    // open the transaction
                    Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Add line to sketch wire"));
                    Gui::cmdAppObjectArgs(sketchgui->getObject(), "addGeometry(Part.LineSegment(App.Vector(%f,%f,0),App.Vector(%f,%f,0)),%s)",
                        EditCurve[0].x,EditCurve[0].y,EditCurve[1].x,EditCurve[1].y,
                        geometryCreationMode==Construction?"True":"False");
                    static_cast<Sketcher::SketchObject*>(sketchgui->getObject())->addGeometry(create_segment(EditCurve[0],EditCurve[1]),geometryCreationMode==Construction);
                }
                catch (const Base::Exception& e) {
                    addedGeometry = false;
                    Base::Console().Error("Failed to add line: %s\n", e.what());
                    Gui::Command::abortCommand();
                }

                firstsegment=false;
            }
            else if (SegmentMode == SEGMENT_MODE_Arc) { // We're dealing with an Arc
                if (!boost::math::isnormal(arcRadius)) {
                    Mode = STATUS_SEEK_Second;
                    return true;
                }

                try {
                    Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Add arc to sketch wire"));
                    Gui::cmdAppObjectArgs(sketchgui->getObject(), "addGeometry(Part.ArcOfCircle"
                        "(Part.Circle(App.Vector(%f,%f,0),App.Vector(0,0,1),%f),%f,%f),%s)",
                        CenterPoint.x, CenterPoint.y, std::abs(arcRadius),
                        std::min(startAngle,endAngle), std::max(startAngle,endAngle),
                        geometryCreationMode==Construction?"True":"False");
                        static_cast<Sketcher::SketchObject*>(sketchgui->getObject())->addGeometry(createArcOfCircle(CenterPoint, std::abs(arcRadius),
                        startAngle, endAngle), geometryCreationMode==Construction);
                }
                catch (const Base::Exception& e) {
                    addedGeometry = false;
                    Base::Console().Error("Failed to add arc: %s\n", e.what());
                    Gui::Command::abortCommand();
                }

                firstsegment=false;
            }

            int lastCurve = getHighestCurveIndex();
            // issue the constraint
            if (addedGeometry && (previousPosId != Sketcher::PointPos::none)) {
                Sketcher::PointPos lastStartPosId = (SegmentMode == SEGMENT_MODE_Arc && startAngle > endAngle) ?
                                                    Sketcher::PointPos::end : Sketcher::PointPos::start;
                Sketcher::PointPos lastEndPosId = (SegmentMode == SEGMENT_MODE_Arc && startAngle > endAngle) ?
                                                  Sketcher::PointPos::start : Sketcher::PointPos::end;
                // in case of a tangency constraint, the coincident constraint is redundant
                std::string constrType = "Coincident";
                Sketcher::ConstraintType ct = Sketcher::Coincident;
                if (!suppressTransition && previousCurve != -1) {
                    if (TransitionMode == TRANSITION_MODE_Tangent) {
                        constrType = "Tangent";
                        ct = Sketcher::ConstraintType::Tangent;
                    }
                    else if (TransitionMode == TRANSITION_MODE_Perpendicular_L ||
                             TransitionMode == TRANSITION_MODE_Perpendicular_R) {
                        constrType = "Perpendicular";
                        ct = Sketcher::ConstraintType::Perpendicular;
                    }
                }
                Gui::cmdAppObjectArgs(sketchgui->getObject(), "addConstraint(Sketcher.Constraint('%s',%i,%i,%i,%i)) ",
                     constrType.c_str(), previousCurve, static_cast<int>(previousPosId), lastCurve, static_cast<int>(lastStartPosId));
                static_cast<Sketcher::SketchObject*>(sketchgui->getObject())->addConstraintEx(create_constraint(ct,
                      previousCurve, static_cast<int>(previousPosId), lastCurve, static_cast<int>(lastStartPosId)));

                if(SnapMode == SNAP_MODE_45Degree && Mode != STATUS_Close) {
                    // -360, -315, -270, -225, -180, -135, -90, -45,  0, 45,  90, 135, 180, 225, 270, 315, 360
                    //  N/A,    a, perp,    a,  par,    a,perp,   a,N/A,  a,perp,   a, par,   a,perp,   a, N/A

                    // #3974: if in radians, the printf %f defaults to six decimals, which leads to loss of precision
                    double arcAngle = abs(round( (endAngle - startAngle) / (M_PI/4)) * 45); // in degrees

                    Gui::cmdAppObjectArgs(sketchgui->getObject(), "addConstraint(Sketcher.Constraint('Angle',%i,App.Units.Quantity('%f deg'))) ",
                                          lastCurve, arcAngle);
                    static_cast<Sketcher::SketchObject*>(sketchgui->getObject())->addConstraintEx(create_constraint(Sketcher::ConstraintType::Angle,
                                          lastCurve, arcAngle));
                }
                if (Mode == STATUS_Close) {
                    // close the loop by constrain to the first curve point
                    Gui::cmdAppObjectArgs(sketchgui->getObject(), "addConstraint(Sketcher.Constraint('Coincident',%i,%i,%i,%i)) ",
                                          lastCurve,static_cast<int>(lastEndPosId),firstCurve,static_cast<int>(firstPosId));
                    static_cast<Sketcher::SketchObject*>(sketchgui->getObject())->addConstraintEx(create_constraint(Sketcher::ConstraintType::Coincident,
                                            lastCurve,static_cast<int>(lastEndPosId),firstCurve,static_cast<int>(firstPosId)));
                }
                Gui::Command::commitCommand();

                tryAutoRecomputeIfNotSolve(static_cast<Sketcher::SketchObject *>(sketchgui->getObject()));
            }

            ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Mod/Sketcher");
            bool avoidredundant = sketchgui->AvoidRedundant.getValue()  && sketchgui->Autoconstraints.getValue();

            if (Mode == STATUS_Close) {

                if(avoidredundant) {
                    if (SegmentMode == SEGMENT_MODE_Line) { // avoid redundant constraints.
                        if (sugConstr1.size() > 0)
                            removeRedundantHorizontalVertical(static_cast<Sketcher::SketchObject *>(sketchgui->getObject()),sugConstr1,sugConstr2);
                        else
                            removeRedundantHorizontalVertical(static_cast<Sketcher::SketchObject *>(sketchgui->getObject()),virtualsugConstr1,sugConstr2);
                    }
                }

                if (sugConstr2.size() > 0) {
                    // exclude any coincidence constraints
                    std::vector<AutoConstraint> sugConstr;
                    for (unsigned int i=0; i < sugConstr2.size(); i++) {
                        if (sugConstr2[i].Type != Sketcher::Coincident)
                            sugConstr.push_back(sugConstr2[i]);
                    }
                    createAutoConstraints(sugConstr, getHighestCurveIndex(), Sketcher::PointPos::end);
                    sugConstr2.clear();
                }

                tryAutoRecomputeIfNotSolve(static_cast<Sketcher::SketchObject *>(sketchgui->getObject()));

                unsetCursor();

                resetPositionText();
                EditCurve.clear();
                drawEdit(EditCurve);

                ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Mod/Sketcher");
                bool continuousMode = hGrp->GetBool("ContinuousCreationMode",true);

                if(continuousMode){
                    // This code enables the continuous creation mode.
                    Mode=STATUS_SEEK_First;
                    SegmentMode=SEGMENT_MODE_Line;
                    TransitionMode=TRANSITION_MODE_Free;
                    SnapMode = SNAP_MODE_Free;
                    suppressTransition=false;
                    firstCurve=-1;
                    previousCurve=-1;
                    firstPosId=Sketcher::PointPos::none;
                    previousPosId=Sketcher::PointPos::none;
                    EditCurve.clear();
                    drawEdit(EditCurve);
                    EditCurve.resize(2);
                    applyCursor();
                    /* this is ok not to call to purgeHandler
                    * in continuous creation mode because the
                    * handler is destroyed by the quit() method on pressing the
                    * right button of the mouse */
                }
                else{
                    sketchgui->purgeHandler(); // no code after this line, Handler get deleted in ViewProvider
                }
            }
            else {
                Gui::Command::commitCommand();

                // Add auto constraints
                if (sugConstr1.size() > 0) { // this is relevant only to the very first point
                    createAutoConstraints(sugConstr1, getHighestCurveIndex(), Sketcher::PointPos::start);
                    sugConstr1.clear();
                }


                if(avoidredundant) {
                    if (SegmentMode == SEGMENT_MODE_Line) { // avoid redundant constraints.
                        if (sugConstr1.size() > 0)
                            removeRedundantHorizontalVertical(static_cast<Sketcher::SketchObject *>(sketchgui->getObject()),sugConstr1,sugConstr2);
                        else
                            removeRedundantHorizontalVertical(static_cast<Sketcher::SketchObject *>(sketchgui->getObject()),virtualsugConstr1,sugConstr2);
                    }
                }

                virtualsugConstr1 = sugConstr2; // these are the initial constraints for the next iteration.

                if (sugConstr2.size() > 0) {
                    createAutoConstraints(sugConstr2, getHighestCurveIndex(),
                                          (SegmentMode == SEGMENT_MODE_Arc && startAngle > endAngle) ?
                                            Sketcher::PointPos::start : Sketcher::PointPos::end);
                    sugConstr2.clear();
                }

                tryAutoRecomputeIfNotSolve(static_cast<Sketcher::SketchObject *>(sketchgui->getObject()));

                // remember the vertex for the next rounds constraint..
                previousCurve = getHighestCurveIndex();
                previousPosId = (SegmentMode == SEGMENT_MODE_Arc && startAngle > endAngle) ?
                                 Sketcher::PointPos::start : Sketcher::PointPos::end; // cw arcs are rendered in reverse

                // setup for the next line segment
                // calculate dirVec and EditCurve[0]
                updateTransitionData(previousCurve,previousPosId);

                applyCursor();
                Mode = STATUS_SEEK_Second;
                if (SegmentMode == SEGMENT_MODE_Arc) {
                    TransitionMode = TRANSITION_MODE_Tangent;
                    EditCurve.resize(3);
                    EditCurve[2] = EditCurve[0];
                }
                else {
                    TransitionMode = TRANSITION_MODE_Free;
                    EditCurve.resize(2);
                }
                SegmentMode = SEGMENT_MODE_Line;
                SnapMode = SNAP_MODE_Free;
                EditCurve[1] = EditCurve[0];
                mouseMove(onSketchPos); // trigger an update of EditCurve
            }
        }
        return true;
    }

    virtual void quit(void) override {
        // We must see if we need to create a B-spline before cancelling everything
        // and now just like any other Handler,

        ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Mod/Sketcher");

        bool continuousMode = hGrp->GetBool("ContinuousCreationMode",true);

        if (firstsegment) {
            // user when right-clicking with no segment in really wants to exit
            DrawSketchHandler::quit();
        }
        else {

            if(!continuousMode){
                DrawSketchHandler::quit();
            }
            else {
                // This code disregards existing data and enables the continuous creation mode.
                Mode=STATUS_SEEK_First;
                SegmentMode=SEGMENT_MODE_Line;
                TransitionMode=TRANSITION_MODE_Free;
                SnapMode = SNAP_MODE_Free;
                suppressTransition=false;
                firstCurve=-1;
                previousCurve=-1;
                firstPosId=Sketcher::PointPos::none;
                previousPosId=Sketcher::PointPos::none;
                firstsegment=true;
                EditCurve.clear();
                drawEdit(EditCurve);
                EditCurve.resize(2);
                applyCursor();
            }
        }
    }

private:
    virtual void activated() override
    {
        setCrosshairCursor("Sketcher_Pointer_Create_Lineset");
    }

protected:
    SELECT_MODE Mode;
    SEGMENT_MODE SegmentMode;
    TRANSITION_MODE TransitionMode;
    SNAP_MODE SnapMode;
    bool suppressTransition;

    std::vector<Base::Vector2d> EditCurve;
    int firstCurve;
    int previousCurve;
    Sketcher::PointPos firstPosId;
    Sketcher::PointPos previousPosId;
    // the latter stores those constraints that a first point would have been given in absence of the transition mechanism
    std::vector<AutoConstraint> sugConstr1, sugConstr2, virtualsugConstr1;

    Base::Vector2d CenterPoint;
    Base::Vector3d dirVec;
    double startAngle, endAngle, arcRadius;

    bool firstsegment;

    void updateTransitionData(int GeoId, Sketcher::PointPos PosId) {

        // Use updated startPoint/endPoint as autoconstraints can modify the position
        const Part::Geometry *geom = sketchgui->getSketchObject()->getGeometry(GeoId);
        if (geom->getTypeId() == Part::GeomLineSegment::getClassTypeId()) {
            const Part::GeomLineSegment *lineSeg = static_cast<const Part::GeomLineSegment *>(geom);
            dirVec.Set(lineSeg->getEndPoint().x - lineSeg->getStartPoint().x,
                       lineSeg->getEndPoint().y - lineSeg->getStartPoint().y,
                       0.f);
            if (PosId == Sketcher::PointPos::start) {
                dirVec *= -1;
                EditCurve[0] = Base::Vector2d(lineSeg->getStartPoint().x, lineSeg->getStartPoint().y);
            }
            else
                EditCurve[0] = Base::Vector2d(lineSeg->getEndPoint().x, lineSeg->getEndPoint().y);
        }
        else if (geom->getTypeId() == Part::GeomArcOfCircle::getClassTypeId()) {
            const Part::GeomArcOfCircle *arcSeg = static_cast<const Part::GeomArcOfCircle *>(geom);
            if (PosId == Sketcher::PointPos::start) {
                EditCurve[0] = Base::Vector2d(arcSeg->getStartPoint(/*emulateCCW=*/true).x,arcSeg->getStartPoint(/*emulateCCW=*/true).y);
                dirVec = Base::Vector3d(0.f,0.f,-1.0) % (arcSeg->getStartPoint(/*emulateCCW=*/true)-arcSeg->getCenter());
            }
            else {
                EditCurve[0] = Base::Vector2d(arcSeg->getEndPoint(/*emulateCCW=*/true).x,arcSeg->getEndPoint(/*emulateCCW=*/true).y);
                dirVec = Base::Vector3d(0.f,0.f,1.0) % (arcSeg->getEndPoint(/*emulateCCW=*/true)-arcSeg->getCenter());
            }
        }
        dirVec.Normalize();
    }
};

DEF_STD_CMD_AU(CmdSketcherCreatePolyline)

CmdSketcherCreatePolyline::CmdSketcherCreatePolyline()
  : Command("Sketcher_CreatePolyline")
{
    sAppModule      = "Sketcher";
    sGroup          = "Sketcher";
    sMenuText       = QT_TR_NOOP("Create polyline");
    sToolTipText    = QT_TR_NOOP("Create a polyline in the sketch. 'M' Key cycles behaviour");
    sWhatsThis      = "Sketcher_CreatePolyline";
    sStatusTip      = sToolTipText;
    sPixmap         = "Sketcher_CreatePolyline";
    sAccel          = "G, M";
    eType           = ForEdit;
}

CONSTRUCTION_UPDATE_ACTION(CmdSketcherCreatePolyline, "Sketcher_CreatePolyline")

void CmdSketcherCreatePolyline::activated(int iMsg)
{
    Q_UNUSED(iMsg);
    ActivateHandler(getActiveGuiDocument(),new DrawSketchHandlerLineSet() );
}

bool CmdSketcherCreatePolyline::isActive(void)
{
    return isCreateGeoActive(getActiveGuiDocument());
}


// ======================================================================================

class DrawSketchHandlerArc : public DrawSketchHandler
{
public:
    DrawSketchHandlerArc()
      : Mode(STATUS_SEEK_First)
      , EditCurve(2)
      , rx(0), ry(0)
      , startAngle(0)
      , endAngle(0)
      , arcAngle(0)
    {
    }
    virtual ~DrawSketchHandlerArc(){}
    /// mode table
    enum SelectMode {
        STATUS_SEEK_First,      /**< enum value ----. */
        STATUS_SEEK_Second,     /**< enum value ----. */
        STATUS_SEEK_Third,      /**< enum value ----. */
        STATUS_End
    };

    virtual void mouseMove(Base::Vector2d onSketchPos) override
    {
        if (Mode==STATUS_SEEK_First) {
            setPositionText(onSketchPos);
            if (seekAutoConstraint(sugConstr1, onSketchPos, Base::Vector2d(0.f,0.f))) {
                renderSuggestConstraintsCursor(sugConstr1);
                return;
            }
        }
        else if (Mode==STATUS_SEEK_Second) {
            double dx_ = onSketchPos.x - EditCurve[0].x;
            double dy_ = onSketchPos.y - EditCurve[0].y;
            for (int i=0; i < 16; i++) {
                double angle = i*M_PI/16.0;
                double dx = dx_ * cos(angle) + dy_ * sin(angle);
                double dy = -dx_ * sin(angle) + dy_ * cos(angle);
                EditCurve[1+i] = Base::Vector2d(EditCurve[0].x + dx, EditCurve[0].y + dy);
                EditCurve[17+i] = Base::Vector2d(EditCurve[0].x - dx, EditCurve[0].y - dy);
            }
            EditCurve[33] = EditCurve[1];

            // Display radius and start angle
            float radius = (onSketchPos - EditCurve[0]).Length();
            float angle = atan2f(dy_ , dx_);

            SbString text;
            text.sprintf(" (%.1fR,%.1fdeg)", radius, angle * 180 / M_PI);
            setPositionText(onSketchPos, text);

            drawEdit(EditCurve);
            if (seekAutoConstraint(sugConstr2, onSketchPos, Base::Vector2d(0.f,0.f))) {
                renderSuggestConstraintsCursor(sugConstr2);
                return;
            }
        }
        else if (Mode==STATUS_SEEK_Third) {
            double angle1 = atan2(onSketchPos.y - CenterPoint.y,
                                 onSketchPos.x - CenterPoint.x) - startAngle;
            double angle2 = angle1 + (angle1 < 0. ? 2 : -2) * M_PI ;
            arcAngle = abs(angle1-arcAngle) < abs(angle2-arcAngle) ? angle1 : angle2;
            for (int i=1; i <= 29; i++) {
                double angle = i*arcAngle/29.0;
                double dx = rx * cos(angle) - ry * sin(angle);
                double dy = rx * sin(angle) + ry * cos(angle);
                EditCurve[i] = Base::Vector2d(CenterPoint.x + dx, CenterPoint.y + dy);
            }

            // Display radius and arc angle
            float radius = (onSketchPos - EditCurve[0]).Length();

            SbString text;
            text.sprintf(" (%.1fR,%.1fdeg)", radius, arcAngle * 180 / M_PI);
            setPositionText(onSketchPos, text);

            drawEdit(EditCurve);
            if (seekAutoConstraint(sugConstr3, onSketchPos, Base::Vector2d(0.0,0.0))) {
                renderSuggestConstraintsCursor(sugConstr3);
                return;
            }
        }
        applyCursor();

    }

    virtual bool pressButton(Base::Vector2d onSketchPos) override
    {
        if (Mode==STATUS_SEEK_First){
            CenterPoint = onSketchPos;
            EditCurve.resize(34);
            EditCurve[0] = onSketchPos;
            Mode = STATUS_SEEK_Second;
        }
        else if (Mode==STATUS_SEEK_Second){
            EditCurve.resize(31);
            EditCurve[0] = onSketchPos;
            EditCurve[30] = CenterPoint;
            rx = EditCurve[0].x - CenterPoint.x;
            ry = EditCurve[0].y - CenterPoint.y;
            startAngle = atan2(ry, rx);
            arcAngle = 0.;
            Mode = STATUS_SEEK_Third;
        }
        else {
            EditCurve.resize(30);
            double angle1 = atan2(onSketchPos.y - CenterPoint.y,
                                 onSketchPos.x - CenterPoint.x) - startAngle;
            double angle2 = angle1 + (angle1 < 0. ? 2 : -2) * M_PI ;
            arcAngle = abs(angle1-arcAngle) < abs(angle2-arcAngle) ? angle1 : angle2;
            if (arcAngle > 0)
                endAngle = startAngle + arcAngle;
            else {
                endAngle = startAngle;
                startAngle += arcAngle;
            }

            drawEdit(EditCurve);
            applyCursor();
            Mode = STATUS_End;
        }

        return true;
    }

    virtual bool releaseButton(Base::Vector2d onSketchPos) override
    {
        Q_UNUSED(onSketchPos);
        if (Mode==STATUS_End) {
            unsetCursor();
            resetPositionText();

            try {
                Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Add sketch arc"));
                Gui::cmdAppObjectArgs(sketchgui->getObject(), "addGeometry(Part.ArcOfCircle"
                    "(Part.Circle(App.Vector(%f,%f,0),App.Vector(0,0,1),%f),%f,%f),%s)",
                          CenterPoint.x, CenterPoint.y, sqrt(rx*rx + ry*ry),
                          startAngle, endAngle,
                          geometryCreationMode==Construction?"True":"False"); //arcAngle > 0 ? 0 : 1);
                // const Part::GeomArcOfCircle* arc = new Part::GeomArcOfCircle();
                const Part::GeomArcOfCircle* arc = createArcOfCircle(CenterPoint, sqrt(rx*rx + ry*ry), startAngle, endAngle);
                static_cast<Sketcher::SketchObject*>(sketchgui->getObject())->addGeometry(arc, geometryCreationMode == Construction);
                Gui::Command::commitCommand();
            }
            catch (const Base::Exception& e) {
                Base::Console().Error("Failed to add arc: %s\n", e.what());
                Gui::Command::abortCommand();
            }

            // Auto Constraint center point
            if (sugConstr1.size() > 0) {
                createAutoConstraints(sugConstr1, getHighestCurveIndex(), Sketcher::PointPos::mid);
                sugConstr1.clear();
            }

            // Auto Constraint first picked point
            if (sugConstr2.size() > 0) {
                createAutoConstraints(sugConstr2, getHighestCurveIndex(), (arcAngle > 0) ? Sketcher::PointPos::start : Sketcher::PointPos::end );
                sugConstr2.clear();
            }

            // Auto Constraint second picked point
            if (sugConstr3.size() > 0) {
                createAutoConstraints(sugConstr3, getHighestCurveIndex(), (arcAngle > 0) ? Sketcher::PointPos::end : Sketcher::PointPos::start);
                sugConstr3.clear();
            }


            tryAutoRecomputeIfNotSolve(static_cast<Sketcher::SketchObject *>(sketchgui->getObject()));

            ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Mod/Sketcher");
            bool continuousMode = hGrp->GetBool("ContinuousCreationMode",true);
            if(continuousMode){
                // This code enables the continuous creation mode.
                Mode=STATUS_SEEK_First;
                EditCurve.clear();
                drawEdit(EditCurve);
                EditCurve.resize(2);
                applyCursor();
                /* this is ok not to call to purgeHandler
                * in continuous creation mode because the
                * handler is destroyed by the quit() method on pressing the
                * right button of the mouse */
            }
            else{
                sketchgui->purgeHandler(); // no code after this line, Handler get deleted in ViewProvider
            }
        }
        return true;
    }

private:
    virtual void activated() override
    {
        setCrosshairCursor("Sketcher_Pointer_Create_Arc");
    }
protected:
    SelectMode Mode;
    std::vector<Base::Vector2d> EditCurve;
    Base::Vector2d CenterPoint;
    double rx, ry, startAngle, endAngle, arcAngle;
    std::vector<AutoConstraint> sugConstr1, sugConstr2, sugConstr3;
};

DEF_STD_CMD_AU(CmdSketcherCreateArc)

CmdSketcherCreateArc::CmdSketcherCreateArc()
  : Command("Sketcher_CreateArc")
{
    sAppModule      = "Sketcher";
    sGroup          = "Sketcher";
    sMenuText       = QT_TR_NOOP("Create arc by center");
    sToolTipText    = QT_TR_NOOP("Create an arc by its center and by its end points");
    sWhatsThis      = "Sketcher_CreateArc";
    sStatusTip      = sToolTipText;
    sPixmap         = "Sketcher_CreateArc";
    sAccel          = "G, A";
    eType           = ForEdit;
}

CONSTRUCTION_UPDATE_ACTION(CmdSketcherCreateArc, "Sketcher_CreateArc")

void CmdSketcherCreateArc::activated(int iMsg)
{
    Q_UNUSED(iMsg);
    ActivateHandler(getActiveGuiDocument(),new DrawSketchHandlerArc() );
}

bool CmdSketcherCreateArc::isActive(void)
{
    return isCreateGeoActive(getActiveGuiDocument());
}


// ======================================================================================

class DrawSketchHandler3PointArc : public DrawSketchHandler
{
public:
    DrawSketchHandler3PointArc()
      : Mode(STATUS_SEEK_First), EditCurve(2)
      , radius(0), startAngle(0)
      , endAngle(0), arcAngle(0)
      , arcPos1(Sketcher::PointPos::none)
      , arcPos2(Sketcher::PointPos::none)
    {
    }
    virtual ~DrawSketchHandler3PointArc(){}
    /// mode table
    enum SelectMode {
        STATUS_SEEK_First,      /**< enum value ----. */
        STATUS_SEEK_Second,     /**< enum value ----. */
        STATUS_SEEK_Third,      /**< enum value ----. */
        STATUS_End
    };

    virtual void mouseMove(Base::Vector2d onSketchPos) override
    {
        if (Mode==STATUS_SEEK_First) {
            setPositionText(onSketchPos);
            if (seekAutoConstraint(sugConstr1, onSketchPos, Base::Vector2d(0.f,0.f))) {
                renderSuggestConstraintsCursor(sugConstr1);
                return;
            }
        }
        else if (Mode==STATUS_SEEK_Second) {
            CenterPoint  = EditCurve[0] = (onSketchPos - FirstPoint)/2 + FirstPoint;
            EditCurve[1] = EditCurve[33] = onSketchPos;
            radius = (onSketchPos - CenterPoint).Length();
            double lineAngle = GetPointAngle(CenterPoint, onSketchPos);

            // Build a 32 point circle ignoring already constructed points
            for (int i=1; i <= 32; i++) {
                // Start at current angle
                double angle = (i-1)*2*M_PI/32.0 + lineAngle; // N point closed circle has N segments
                if (i != 1 && i != 17 ) {
                    EditCurve[i] = Base::Vector2d(CenterPoint.x + radius*cos(angle),
                                                  CenterPoint.y + radius*sin(angle));
                }
            }

            // Display radius and start angle
            // This lineAngle will report counter-clockwise from +X, not relatively
            SbString text;
            text.sprintf(" (%.1fR,%.1fdeg)", (float) radius, (float) lineAngle * 180 / M_PI);
            setPositionText(onSketchPos, text);

            drawEdit(EditCurve);
            if (seekAutoConstraint(sugConstr2, onSketchPos, Base::Vector2d(0.f,0.f))) {
                renderSuggestConstraintsCursor(sugConstr2);
                return;
            }
        }
        else if (Mode==STATUS_SEEK_Third) {
            /*
            Centerline inverts when the arc flips sides.  Easily taken care of by replacing
            centerline with a point.  It happens because the direction the curve is being drawn
            reverses.
            */
            try {
                CenterPoint = EditCurve[30] = Part::Geom2dCircle::getCircleCenter(FirstPoint, SecondPoint, onSketchPos);

                radius = (SecondPoint - CenterPoint).Length();

                double angle1 = GetPointAngle(CenterPoint, FirstPoint);
                double angle2 = GetPointAngle(CenterPoint, SecondPoint);
                double angle3 = GetPointAngle(CenterPoint, onSketchPos);

                // Always build arc counter-clockwise
                // Point 3 is between Point 1 and 2
                if ( angle3 > min(angle1, angle2) && angle3 < max(angle1, angle2) ) {
                    if (angle2 > angle1) {
                        EditCurve[0] =  FirstPoint;
                        EditCurve[29] = SecondPoint;
                        arcPos1 = Sketcher::PointPos::start;
                        arcPos2 = Sketcher::PointPos::end;
                    }
                    else {
                        EditCurve[0] =  SecondPoint;
                        EditCurve[29] = FirstPoint;
                        arcPos1 = Sketcher::PointPos::end;
                        arcPos2 = Sketcher::PointPos::start;
                    }
                    startAngle = min(angle1, angle2);
                    endAngle   = max(angle1, angle2);
                    arcAngle = endAngle - startAngle;
                }
                // Point 3 is not between Point 1 and 2
                else {
                    if (angle2 > angle1) {
                        EditCurve[0] =  SecondPoint;
                        EditCurve[29] = FirstPoint;
                        arcPos1 = Sketcher::PointPos::end;
                        arcPos2 = Sketcher::PointPos::start;
                    }
                    else {
                        EditCurve[0] =  FirstPoint;
                        EditCurve[29] = SecondPoint;
                        arcPos1 = Sketcher::PointPos::start;
                        arcPos2 = Sketcher::PointPos::end;
                    }
                    startAngle = max(angle1, angle2);
                    endAngle   = min(angle1, angle2);
                    arcAngle = 2*M_PI - (startAngle - endAngle);
                }

                // Build a 30 point circle ignoring already constructed points
                for (int i=1; i <= 28; i++) {
                    double angle = startAngle + i*arcAngle/29.0; // N point arc has N-1 segments
                    EditCurve[i] = Base::Vector2d(CenterPoint.x + radius*cos(angle),
                                                CenterPoint.y + radius*sin(angle));
                }

                SbString text;
                text.sprintf(" (%.1fR,%.1fdeg)", (float) radius, (float) arcAngle * 180 / M_PI);
                setPositionText(onSketchPos, text);

                drawEdit(EditCurve);
                if (seekAutoConstraint(sugConstr3, onSketchPos, Base::Vector2d(0.0,0.0),
                                    AutoConstraint::CURVE)) {
                    renderSuggestConstraintsCursor(sugConstr3);
                    return;
                }
            }
            catch(Base::ValueError &e) {
                e.ReportException();
            }
        }
        applyCursor();
    }

    virtual bool pressButton(Base::Vector2d onSketchPos) override
    {
        if (Mode==STATUS_SEEK_First){
            // 32 point curve + center + endpoint
            EditCurve.resize(34);
            // 17 is circle halfway point (1+32/2)
            FirstPoint = EditCurve[17] = onSketchPos;

            Mode = STATUS_SEEK_Second;
        }
        else if (Mode==STATUS_SEEK_Second){
            // 30 point arc and center point
            EditCurve.resize(31);
            SecondPoint = onSketchPos;

            Mode = STATUS_SEEK_Third;
        }
        else {
            EditCurve.resize(30);

            drawEdit(EditCurve);
            applyCursor();
            Mode = STATUS_End;
        }

        return true;
    }

    virtual bool releaseButton(Base::Vector2d onSketchPos) override
    {
        Q_UNUSED(onSketchPos);
        // Need to look at.  rx might need fixing.
        if (Mode==STATUS_End) {
            unsetCursor();
            resetPositionText();

            try {
                Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Add sketch arc"));
                Gui::cmdAppObjectArgs(sketchgui->getObject(), "addGeometry(Part.ArcOfCircle"
                    "(Part.Circle(App.Vector(%f,%f,0),App.Vector(0,0,1),%f),%f,%f),%s)",
                          CenterPoint.x, CenterPoint.y, radius,
                          startAngle, endAngle,
                          geometryCreationMode==Construction?"True":"False");
                const Part::GeomArcOfCircle* arc = createArcOfCircle(CenterPoint, radius, startAngle, endAngle);
                static_cast<Sketcher::SketchObject*>(sketchgui->getObject())->addGeometry(arc, geometryCreationMode == Construction);
                Gui::Command::commitCommand();
            }
            catch (const Base::Exception& e) {
                Base::Console().Error("Failed to add arc: %s\n", e.what());
                Gui::Command::abortCommand();
            }

            // Auto Constraint first picked point
            if (sugConstr1.size() > 0) {
                createAutoConstraints(sugConstr1, getHighestCurveIndex(), arcPos1);
                sugConstr1.clear();
            }

            // Auto Constraint second picked point
            if (sugConstr2.size() > 0) {
                createAutoConstraints(sugConstr2, getHighestCurveIndex(), arcPos2);
                sugConstr2.clear();
            }

            // Auto Constraint third picked point
            if (sugConstr3.size() > 0) {
                createAutoConstraints(sugConstr3, getHighestCurveIndex(), Sketcher::PointPos::none);
                sugConstr3.clear();
            }

            tryAutoRecomputeIfNotSolve(static_cast<Sketcher::SketchObject *>(sketchgui->getObject()));

            ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Mod/Sketcher");
            bool continuousMode = hGrp->GetBool("ContinuousCreationMode",true);
            if(continuousMode){
                // This code enables the continuous creation mode.
                Mode=STATUS_SEEK_First;
                EditCurve.clear();
                drawEdit(EditCurve);
                EditCurve.resize(2);
                applyCursor();
                /* this is ok not to call to purgeHandler
                * in continuous creation mode because the
                * handler is destroyed by the quit() method on pressing the
                * right button of the mouse */
            }
            else{
                sketchgui->purgeHandler(); // no code after this line, Handler get deleted in ViewProvider
            }
        }
        return true;
    }

private:
    virtual void activated() override
    {
        setCrosshairCursor("Sketcher_Pointer_Create_3PointArc");
    }

protected:
    SelectMode Mode;
    std::vector<Base::Vector2d> EditCurve;
    Base::Vector2d CenterPoint, FirstPoint, SecondPoint;
    double radius, startAngle, endAngle, arcAngle;
    std::vector<AutoConstraint> sugConstr1, sugConstr2, sugConstr3;
    Sketcher::PointPos arcPos1, arcPos2;
};

DEF_STD_CMD_AU(CmdSketcherCreate3PointArc)

CmdSketcherCreate3PointArc::CmdSketcherCreate3PointArc()
  : Command("Sketcher_Create3PointArc")
{
    sAppModule      = "Sketcher";
    sGroup          = "Sketcher";
    sMenuText       = QT_TR_NOOP("Create arc by three points");
    sToolTipText    = QT_TR_NOOP("Create an arc by its end points and a point along the arc");
    sWhatsThis      = "Sketcher_Create3PointArc";
    sStatusTip      = sToolTipText;
    sPixmap         = "Sketcher_Create3PointArc";
    sAccel          = "G, 3, A";
    eType           = ForEdit;
}

CONSTRUCTION_UPDATE_ACTION(CmdSketcherCreate3PointArc, "Sketcher_Create3PointArc")

void CmdSketcherCreate3PointArc::activated(int iMsg)
{
    Q_UNUSED(iMsg);
    ActivateHandler(getActiveGuiDocument(),new DrawSketchHandler3PointArc() );
}

bool CmdSketcherCreate3PointArc::isActive(void)
{
    return isCreateGeoActive(getActiveGuiDocument());
}


DEF_STD_CMD_ACLU(CmdSketcherCompCreateArc)

CmdSketcherCompCreateArc::CmdSketcherCompCreateArc()
  : Command("Sketcher_CompCreateArc")
{
    sAppModule      = "Sketcher";
    sGroup          = "Sketcher";
    sMenuText       = QT_TR_NOOP("Create arc");
    sToolTipText    = QT_TR_NOOP("Create an arc in the sketcher");
    sWhatsThis      = "Sketcher_CompCreateArc";
    sStatusTip      = sToolTipText;
    eType           = ForEdit;
}

void CmdSketcherCompCreateArc::activated(int iMsg)
{
    if (iMsg==0)
        ActivateHandler(getActiveGuiDocument(),new DrawSketchHandlerArc());
    else if (iMsg==1)
        ActivateHandler(getActiveGuiDocument(),new DrawSketchHandler3PointArc());
    else
        return;

    // Since the default icon is reset when enabling/disabling the command we have
    // to explicitly set the icon of the used command.
    Gui::ActionGroup* pcAction = dynamic_cast<Gui::ActionGroup*>(_pcAction);
    QList<QAction*> a = pcAction->actions();

    assert(iMsg < a.size());
    pcAction->setIcon(a[iMsg]->icon());
}

Gui::Action * CmdSketcherCompCreateArc::createAction(void)
{
    Gui::ActionGroup* pcAction = new Gui::ActionGroup(this, Gui::getMainWindow());
    pcAction->setDropDownMenu(true);
    applyCommandData(this->className(), pcAction);

    QAction* arc1 = pcAction->addAction(QString());
    arc1->setIcon(Gui::BitmapFactory().iconFromTheme("Sketcher_CreateArc"));
    QAction* arc2 = pcAction->addAction(QString());
    arc2->setIcon(Gui::BitmapFactory().iconFromTheme("Sketcher_Create3PointArc"));

    _pcAction = pcAction;
    languageChange();

    pcAction->setIcon(arc1->icon());
    int defaultId = 0;
    pcAction->setProperty("defaultAction", QVariant(defaultId));

    return pcAction;
}

void CmdSketcherCompCreateArc::updateAction(int mode)
{
    Gui::ActionGroup* pcAction = dynamic_cast<Gui::ActionGroup*>(getAction());
    if (!pcAction)
        return;

    QList<QAction*> a = pcAction->actions();
    int index = 0;// = pcAction->property("defaultAction").toInt();
    switch (mode) {
    case Normal:
        a[0]->setIcon(Gui::BitmapFactory().iconFromTheme("Sketcher_CreateArc"));
        a[1]->setIcon(Gui::BitmapFactory().iconFromTheme("Sketcher_Create3PointArc"));
        getAction()->setIcon(a[index]->icon());
        break;
    case Construction:
        a[0]->setIcon(Gui::BitmapFactory().iconFromTheme("Sketcher_CreateArc_Constr"));
        a[1]->setIcon(Gui::BitmapFactory().iconFromTheme("Sketcher_Create3PointArc_Constr"));
        getAction()->setIcon(a[index]->icon());
        break;
    }
}

void CmdSketcherCompCreateArc::languageChange()
{
    Command::languageChange();

    if (!_pcAction)
        return;
    Gui::ActionGroup* pcAction = dynamic_cast<Gui::ActionGroup*>(_pcAction);
    QList<QAction*> a = pcAction->actions();

    QAction* arc1 = a[0];
    arc1->setText(QApplication::translate("CmdSketcherCompCreateArc","Center and end points"));
    arc1->setToolTip(QApplication::translate("Sketcher_CreateArc","Create an arc by its center and by its end points"));
    arc1->setStatusTip(QApplication::translate("Sketcher_CreateArc","Create an arc by its center and by its end points"));
    QAction* arc2 = a[1];
    arc2->setText(QApplication::translate("CmdSketcherCompCreateArc","End points and rim point"));
    arc2->setToolTip(QApplication::translate("Sketcher_Create3PointArc","Create an arc by its end points and a point along the arc"));
    arc2->setStatusTip(QApplication::translate("Sketcher_Create3PointArc","Create an arc by its end points and a point along the arc"));
}

bool CmdSketcherCompCreateArc::isActive(void)
{
    return isCreateGeoActive(getActiveGuiDocument());
}


// ======================================================================================

class DrawSketchHandlerCircle : public DrawSketchHandler
{
public:
    DrawSketchHandlerCircle() : Mode(STATUS_SEEK_First),EditCurve(34){}
    virtual ~DrawSketchHandlerCircle(){}
    /// mode table
    enum SelectMode {
        STATUS_SEEK_First,      /**< enum value ----. */
        STATUS_SEEK_Second,     /**< enum value ----. */
        STATUS_Close
    };

    virtual void mouseMove(Base::Vector2d onSketchPos) override
    {
        if (Mode==STATUS_SEEK_First) {
            setPositionText(onSketchPos);
            if (seekAutoConstraint(sugConstr1, onSketchPos, Base::Vector2d(0.f,0.f))) {
                renderSuggestConstraintsCursor(sugConstr1);
                return;
            }
        }
        else if (Mode==STATUS_SEEK_Second) {
            double rx0 = onSketchPos.x - EditCurve[0].x;
            double ry0 = onSketchPos.y - EditCurve[0].y;
            for (int i=0; i < 16; i++) {
                double angle = i*M_PI/16.0;
                double rx = rx0 * cos(angle) + ry0 * sin(angle);
                double ry = -rx0 * sin(angle) + ry0 * cos(angle);
                EditCurve[1+i] = Base::Vector2d(EditCurve[0].x + rx, EditCurve[0].y + ry);
                EditCurve[17+i] = Base::Vector2d(EditCurve[0].x - rx, EditCurve[0].y - ry);
            }
            EditCurve[33] = EditCurve[1];

            // Display radius for user
            float radius = (onSketchPos - EditCurve[0]).Length();

            SbString text;
            text.sprintf(" (%.1fR)", radius);
            setPositionText(onSketchPos, text);

            drawEdit(EditCurve);
            if (seekAutoConstraint(sugConstr2, onSketchPos, onSketchPos - EditCurve[0],
                                   AutoConstraint::CURVE)) {
                renderSuggestConstraintsCursor(sugConstr2);
                return;
            }
        }
        applyCursor();
    }

    virtual bool pressButton(Base::Vector2d onSketchPos) override
    {
        if (Mode==STATUS_SEEK_First){
            EditCurve[0] = onSketchPos;
            Mode = STATUS_SEEK_Second;
        } else {
            EditCurve[1] = onSketchPos;
            Mode = STATUS_Close;
        }
        return true;
    }

    virtual bool releaseButton(Base::Vector2d onSketchPos) override
    {
        Q_UNUSED(onSketchPos);
        if (Mode==STATUS_Close) {
            double rx = EditCurve[1].x - EditCurve[0].x;
            double ry = EditCurve[1].y - EditCurve[0].y;
            unsetCursor();
            resetPositionText();

            try {
                Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Add sketch circle"));
                Gui::cmdAppObjectArgs(sketchgui->getObject(), "addGeometry(Part.Circle"
                    "(App.Vector(%f,%f,0),App.Vector(0,0,1),%f),%s)",
                          EditCurve[0].x, EditCurve[0].y,
                          sqrt(rx*rx + ry*ry),
                          geometryCreationMode==Construction?"True":"False");
                const Part::GeomCircle* circle = create_circle(EditCurve[0], sqrt(rx*rx + ry*ry));
                static_cast<Sketcher::SketchObject*>(sketchgui->getObject())->addGeometry(circle, geometryCreationMode == Construction);
                Gui::Command::commitCommand();
            }
            catch (const Base::Exception& e) {
                Base::Console().Error("Failed to add circle: %s\n", e.what());
                Gui::Command::abortCommand();
            }

            // add auto constraints for the center point
            if (sugConstr1.size() > 0) {
                createAutoConstraints(sugConstr1, getHighestCurveIndex(), Sketcher::PointPos::mid);
                sugConstr1.clear();
            }

            // add suggested constraints for circumference
            if (sugConstr2.size() > 0) {
                createAutoConstraints(sugConstr2, getHighestCurveIndex(), Sketcher::PointPos::none);
                sugConstr2.clear();
            }

            tryAutoRecomputeIfNotSolve(static_cast<Sketcher::SketchObject *>(sketchgui->getObject()));

            ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Mod/Sketcher");
            bool continuousMode = hGrp->GetBool("ContinuousCreationMode",true);
            if(continuousMode){
                // This code enables the continuous creation mode.
                Mode=STATUS_SEEK_First;
                EditCurve.clear();
                drawEdit(EditCurve);
                EditCurve.resize(34);
                applyCursor();
                /* this is ok not to call to purgeHandler
                * in continuous creation mode because the
                * handler is destroyed by the quit() method on pressing the
                * right button of the mouse */
            }
            else{
                sketchgui->purgeHandler(); // no code after this line, Handler get deleted in ViewProvider
            }
        }
        return true;
    }

private:
    virtual void activated() override
    {
        setCrosshairCursor("Sketcher_Pointer_Create_Circle");
    }

protected:
    SelectMode Mode;
    std::vector<Base::Vector2d> EditCurve;
    std::vector<AutoConstraint> sugConstr1, sugConstr2;

};

DEF_STD_CMD_AU(CmdSketcherCreateCircle)

CmdSketcherCreateCircle::CmdSketcherCreateCircle()
  : Command("Sketcher_CreateCircle")
{
    sAppModule      = "Sketcher";
    sGroup          = "Sketcher";
    sMenuText       = QT_TR_NOOP("Create circle");
    sToolTipText    = QT_TR_NOOP("Create a circle in the sketch");
    sWhatsThis      = "Sketcher_CreateCircle";
    sStatusTip      = sToolTipText;
    sPixmap         = "Sketcher_CreateCircle";
    sAccel          = "G, C";
    eType           = ForEdit;
}

CONSTRUCTION_UPDATE_ACTION(CmdSketcherCreateCircle, "Sketcher_CreateCircle")

void CmdSketcherCreateCircle::activated(int iMsg)
{
    Q_UNUSED(iMsg);
    ActivateHandler(getActiveGuiDocument(),new DrawSketchHandlerCircle() );
}

bool CmdSketcherCreateCircle::isActive(void)
{
    return isCreateGeoActive(getActiveGuiDocument());
}
// ======================================================================================

/**
 * @brief This class handles user interaction to draw and save the ellipse
 *
 * Two construction methods are implemented:
 *   -Periapsis, apoapsis, and b; and
 *   -Center, periapsis, and b.
 *
 * The first method limits the ellipse to a circle, while the second method allows for
 * swapping of the semi-major and semi-minor axes.
 *
 * We use three reference frames in this class.  The first (and primary), is the cartesian
 * frame of the sketcher; all our work begins and ends in this frame.  The second is the
 * perifocal frame of the ellipse using polar coordinates.  We use this frame for naming
 * conventions and working with the ellipse.  The last is a rotated right-handed cartesian
 * frame centered at the ellipse center with the +X direction towards periapsis, +Z out of
 * screen.
 *
 * When working with an ellipse in the perifocal frame, the following equations are useful:
 *
 *    \f{eqnarray*}{
 *        r &\equiv& \textrm{ radial distance from the focus to a point on the ellipse}\\
 *        r_a &\equiv& \textrm{ radial distance from the focus to apopasis}\\
 *        r_p &\equiv& \textrm{ radial distance from the focus to periapsis}\\
 *        a &\equiv& \textrm{ length of the semi-major axis, colloquially 'radius'}\\
 *        b &\equiv& \textrm{ length of the semi-minor axis, colloquially 'radius'}\\
 *        e &\equiv& \textrm{ eccentricity of the ellipse}\\
 *        \theta_b &\equiv& \textrm{ angle to the intersection of the semi-minor axis and the ellipse, relative to the focus}\\
 *        ae &\equiv& \textrm{ distance from the focus to the centroid}\\
 *        r &=& \frac{a(1-e^2)}{1+e\cos(\theta)} = \frac{r_a(1-e)}{1+e\cos(\theta)} = \frac{r_p(1+e)}{1+e\cos(\theta)}\\
 *        r_a &=& a(1-e)\\
 *        r_p &=& a(1+e)\\
 *        a &=& \frac{r_p+r_a}{2}\\
 *        b &=& a\sqrt{1-e^2}\\
 *        e &=& \frac{r_a-r_p}{r_a+r_p} = \sqrt{1-\frac{b^2}{a^2}}\\
 *        \theta_b &=& \left[\pi - \arctan\left(\frac{b}{ae}\right)\right] \pm N\pi
 *   \f}
 *
 */
class DrawSketchHandlerEllipse : public DrawSketchHandler
{
public:
    DrawSketchHandlerEllipse(int constructionMethod)
      : mode(STATUS_Close)
      , method(CENTER_PERIAPSIS_B)
      , constrMethod(constructionMethod)
      , a(0), b(0), e(0), ratio(0), ae(0)
      , num(0), r(0), theta(0), phi(0)
      , editCurve(33), fixedAxisLength(0)
    {
    }
    virtual ~DrawSketchHandlerEllipse(){}
    /// Mode table, describes what step of the process we are in
    enum SelectMode {
        STATUS_SEEK_PERIAPSIS,  /**< enum value, looking for click to set periapsis. */
        STATUS_SEEK_APOAPSIS,   /**< enum value, looking for click to set apoapsis. */
        STATUS_SEEK_CENTROID,   /**< enum value, looking for click to set centroid. */
        STATUS_SEEK_A,          /**< enum value, looking for click to set a. */
        STATUS_SEEK_B,          /**< enum value, looking for click to set b. */
        STATUS_Close            /**< enum value, finalizing and saving ellipse. */
    };
    /// Construction methods, describes the method used to construct the ellipse
    enum ConstructionMethod {
        CENTER_PERIAPSIS_B,     /**< enum value, click on center, then periapsis, then b point. */
        PERIAPSIS_APOAPSIS_B    /**< enum value, click on periapsis, then apoapsis, then b point. */
    };

    /**
     * @brief Updates the ellipse when the cursor moves
     * @param onSketchPos the position of the cursor on the sketch
     */
    virtual void mouseMove(Base::Vector2d onSketchPos) override
    {
        if (method == PERIAPSIS_APOAPSIS_B) {
            if (mode == STATUS_SEEK_PERIAPSIS) {
                setPositionText(onSketchPos);
                if (seekAutoConstraint(sugConstr1, onSketchPos, Base::Vector2d(0.f,0.f),
                    AutoConstraint::CURVE)) {
                    renderSuggestConstraintsCursor(sugConstr1);
                    return;
                }
            } else if (mode == STATUS_SEEK_APOAPSIS) {
                solveEllipse(onSketchPos);
                approximateEllipse();

                // Display radius for user
                float semiMajorRadius = a * 2;
                SbString text;
                text.sprintf(" (%.1fR,%.1fR)", semiMajorRadius,semiMajorRadius);
                setPositionText(onSketchPos, text);

                drawEdit(editCurve);
                // Suggestions for ellipse and curves are disabled because many tangent constraints
                // need an intermediate point or line.
                if (seekAutoConstraint(sugConstr2, onSketchPos, Base::Vector2d(0.f,0.f),
                    AutoConstraint::CURVE)) {
                    renderSuggestConstraintsCursor(sugConstr2);
                    return;
                }
            } else if (mode == STATUS_SEEK_B) {
                solveEllipse(onSketchPos);
                approximateEllipse();

                // Display radius for user
                SbString text;
                text.sprintf(" (%.1fR,%.1fR)", a, b);
                setPositionText(onSketchPos, text);

                drawEdit(editCurve);
                if (seekAutoConstraint(sugConstr3, onSketchPos, Base::Vector2d(0.f,0.f),
                    AutoConstraint::CURVE)) {
                    renderSuggestConstraintsCursor(sugConstr3);
                    return;
                }
            }
        } else { // method is CENTER_PERIAPSIS_B
            if (mode == STATUS_SEEK_CENTROID) {
                setPositionText(onSketchPos);
                if (seekAutoConstraint(sugConstr1, onSketchPos, Base::Vector2d(0.f,0.f))) { // TODO: ellipse prio 1
                    renderSuggestConstraintsCursor(sugConstr1);
                    return;
                }
            } else if (mode == STATUS_SEEK_PERIAPSIS) {
                solveEllipse(onSketchPos);
                approximateEllipse();

                // Display radius for user
                float semiMajorRadius = a * 2;
                SbString text;
                text.sprintf(" (%.1fR,%.1fR)", semiMajorRadius,semiMajorRadius);
                setPositionText(onSketchPos, text);

                drawEdit(editCurve);
                if (seekAutoConstraint(sugConstr2, onSketchPos, onSketchPos - centroid,
                    AutoConstraint::CURVE)) {
                    renderSuggestConstraintsCursor(sugConstr2);
                    return;
                }
            } else if ((mode == STATUS_SEEK_A) || (mode == STATUS_SEEK_B)) {
                solveEllipse(onSketchPos);
                approximateEllipse();

                // Display radius for user
                SbString text;
                text.sprintf(" (%.1fR,%.1fR)", a, b);
                setPositionText(onSketchPos, text);

                drawEdit(editCurve);
                if (seekAutoConstraint(sugConstr3, onSketchPos, onSketchPos - centroid,
                    AutoConstraint::CURVE)) {
                    renderSuggestConstraintsCursor(sugConstr3);
                    return;
                }
            }
        }
        applyCursor();
    }

    /**
     * @brief Changes drawing mode on user-click
     * @param onSketchPos the position of the cursor on the sketch
     * @return
     */
    virtual bool pressButton(Base::Vector2d onSketchPos) override
    {
        if (method == PERIAPSIS_APOAPSIS_B) {
            if (mode == STATUS_SEEK_PERIAPSIS) {
                periapsis = onSketchPos;
                mode = STATUS_SEEK_APOAPSIS;
            }
            else if (mode == STATUS_SEEK_APOAPSIS) {
                apoapsis = onSketchPos;
                mode = STATUS_SEEK_B;
            }
            else {
                mode = STATUS_Close;
            }
        } else { // method is CENTER_PERIAPSIS_B
            if (mode == STATUS_SEEK_CENTROID) {
                centroid = onSketchPos;
                mode = STATUS_SEEK_PERIAPSIS;
            }
            else if (mode == STATUS_SEEK_PERIAPSIS) {
                periapsis = onSketchPos;
                mode = STATUS_SEEK_B;
            }
            else {
                mode = STATUS_Close;
            }
        }
        return true;
    }

    /**
     * @brief Calls \c saveEllipse() after last user input
     * @param onSketchPos the position of the cursor on the sketch
     * @return
     */
    virtual bool releaseButton(Base::Vector2d onSketchPos) override
    {
        Q_UNUSED(onSketchPos);
        if (mode == STATUS_Close) {
            saveEllipse();
            ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Mod/Sketcher");
            bool continuousMode = hGrp->GetBool("ContinuousCreationMode",true);

            if(continuousMode){
                if (constrMethod == 0) {
                    method = CENTER_PERIAPSIS_B;
                    mode = STATUS_SEEK_CENTROID;
                } else {
                    method = PERIAPSIS_APOAPSIS_B;
                    mode = STATUS_SEEK_PERIAPSIS;
                }
            }
        }
        return true;
    }

private:
    /**
     * @brief Slot called when the create ellipse command is activated
     * @param sketchgui A pointer to the active sketch
     */
    virtual void activated() override
    {
        setCrosshairCursor("Sketcher_Pointer_Create_Ellipse");
        if (constrMethod == 0) {
            method = CENTER_PERIAPSIS_B;
            mode = STATUS_SEEK_CENTROID;
        } else {
            method = PERIAPSIS_APOAPSIS_B;
            mode = STATUS_SEEK_PERIAPSIS;
        }
    }

protected:
    std::vector<AutoConstraint> sugConstr1, sugConstr2, sugConstr3;
private:
    SelectMode mode;
    /// the method of constructing the ellipse
    ConstructionMethod method;
    int constrMethod;
    /// periapsis position vector, in standard position in sketch coordinate system
    Base::Vector2d periapsis;
    /// apoapsis position vector, in standard position in sketch coordinate system
    Base::Vector2d apoapsis;
    /// centroid position vector, in standard position in sketch coordinate system
    Base::Vector2d centroid;
    /**
     * @brief position vector of positive b point, in standard position in sketch coordinate system
     * I.E. in polar perifocal system, the first intersection of the semiminor axis with the ellipse
     * as theta increases from 0. This always happens when:
     *    \f{eqnarray*}{
     *        \theta_b &=& \left[\pi - \arctan\left(\frac{b}{ae}\right)\right]  \pm N 2\pi
     *   \f}
     *
     * In a rotated R^3 cartesian system, centered at the centroid, +X towards periapsis, and
     * +Z coming out of the sketch, this b position is in the +Y direction from the centroid.
     */
    Base::Vector2d positiveB;
    /// the other b position
    Base::Vector2d negativeB;
    /// cart. position vector for primary focus
    Base::Vector2d f;
    /// cart. position vector for other focus
    Base::Vector2d fPrime;
    /// Unit vector for apse line
    Base::Vector2d apseHat;
    /// length of semimajor axis, i.e. 'radius' colloquially
    double a;
    /// length of semiminor axis, i.e. 'radius' colloquially
    double b;
    /// eccentricity [unitless]
    double e;
    /// optimization, holds a term that helps calculate b in terms of a and e
    double ratio;
    /// holds product of a * e
    double ae;
    /// holds numerator of orbit equation of form a(1-e^2)
    double num;
    /// holds a radial distance from f to the ellipse for a given theta
    double r;
    /// angle of a point in a perifocal frame centered at f
    double theta;
    /// angle of apse line relative to sketch coordinate system
    double phi;
    /// holds a position vector for a point on the ellipse from f
    Base::Vector2d pos;
    /// holds a position vector for a point on the ellipse from fPrime
    Base::Vector2d posPrime;
    /// holds position vectors for a points on the ellipse
    std::vector<Base::Vector2d> editCurve;
    /// local i_hat vector for ellipse, from centroid to periapsis
    Base::Vector3d iPrime;
    /// local j_hat vector for ellipse, from centroid to b point
    Base::Vector3d jPrime;
    /// length (radius) of the fixed axis
    double fixedAxisLength;
    /// position vector of fixed axis point in sketch coordinates
    Base::Vector2d fixedAxis;

    /**
     * @brief Computes a vector of 2D points representing an ellipse
     * @param onSketchPos Current position of the cursor on the sketch
     */
    void solveEllipse(Base::Vector2d onSketchPos)
    {
        const double GOLDEN_RATIO = 1.6180339887;
        Base::Vector3d k(0,0,1);

        if (method == PERIAPSIS_APOAPSIS_B) {
            if (mode == STATUS_SEEK_APOAPSIS) {
                apoapsis = onSketchPos;
            }
            a = (apoapsis - periapsis).Length() / 2;
            apseHat = (periapsis - apoapsis);
            apseHat.Normalize();
            centroid = apseHat;
            centroid.Scale(-1 * a);
            centroid = periapsis + centroid;
            if (mode == STATUS_SEEK_APOAPSIS) {
                // for first step, we draw an ellipse inscribed in a golden rectangle
                ratio = 1 / GOLDEN_RATIO;   // ~= 0.6180339887
                e = sqrt(ratio);            // ~= 0.7861513777
                b = a * ratio;
            }
            else if (mode == STATUS_SEEK_B) {
                // Get the closest distance from onSketchPos to apse line, as a 'requested' value for b
                Base::Vector2d cursor = Base::Vector2d(onSketchPos - f); // vector from f to cursor pos
                // decompose cursor with a projection, then length of w_2 will give us b
                Base::Vector2d w_1 = cursor;
                w_1.ProjectToLine(cursor, (periapsis - apoapsis)); // projection of cursor line onto apse line
                Base::Vector2d w_2 = (cursor - w_1);
                b = w_2.Length();

                // limit us to ellipse or circles
                if (b > a) {
                    b = a;
                }

                e = sqrt(1 - ((b * b) / (a * a)));
                ratio = sqrt(1 - (e*e));
            }
            ae = a * e;
            f = apseHat;
            f.Scale(ae);
            f = centroid + f;
            fPrime = apseHat;
            fPrime.Scale(-1 * ae);
            fPrime = centroid + fPrime;
            phi = atan2(apseHat.y, apseHat.x);
            num = a * (1 - (e * e));
            // The ellipse is now solved
        } else { // method == CENTER_PERIAPSIS_B
            if (mode == STATUS_SEEK_PERIAPSIS) {
                // solve the ellipse inscribed in a golden rectangle
                periapsis = onSketchPos;
                a = (centroid - periapsis).Length();
                iPrime.x = periapsis.x - centroid.x;
                iPrime.y = periapsis.y - centroid.y;
                iPrime.z = 0;
                jPrime = k % iPrime;   // j = k cross i

                // these are constant for any ellipse inscribed in a golden rectangle
                ratio = 1 / GOLDEN_RATIO;   // ~= 0.6180339887
                e = sqrt(ratio);            // ~= 0.7861513777

                b = a * ratio;
                ae = a * e;
                apseHat = (periapsis - centroid);
                apseHat.Normalize();
                f = apseHat;
                f.Scale(ae);
                f = centroid + f;
                fPrime = apseHat;
                fPrime.Scale(-1 * ae);
                fPrime = centroid + fPrime;
                apoapsis = apseHat;
                apoapsis.Scale(-1 * a);
                apoapsis = centroid + apoapsis;
                phi = atan2(apseHat.y, apseHat.x);
                num = a * (1 - (e * e));
                fixedAxisLength = a;
                fixedAxis = periapsis;
            } else if ((mode == STATUS_SEEK_B) || (mode == STATUS_SEEK_A)) {
                // while looking for the last click, we may switch back and forth
                // between looking for a b point and looking for periapsis, so ensure
                // we are in the right mode
                Base::Vector2d cursor = Base::Vector2d(onSketchPos - centroid); // vector from centroid to cursor pos
                // decompose cursor with a projection, then length of w_2 will give us b
                Base::Vector2d w_1 = cursor;
                w_1.ProjectToLine(cursor, (fixedAxis - centroid)); // projection of cursor line onto fixed axis line
                Base::Vector2d w_2 = (cursor - w_1);
                if (w_2.Length() > fixedAxisLength) {
                    // b is fixed, we are seeking a
                    mode = STATUS_SEEK_A;
                    jPrime.x = (fixedAxis - centroid).x;
                    jPrime.y = (fixedAxis - centroid).y;
                    jPrime.Normalize();
                    iPrime = jPrime % k;    // cross
                    b = fixedAxisLength;
                    a = w_2.Length();
                } else {
                    // a is fixed, we are seeking b
                    mode = STATUS_SEEK_B;
                    iPrime.x = (fixedAxis - centroid).x;
                    iPrime.y = (fixedAxis - centroid).y;
                    iPrime.Normalize();
                    jPrime = k % iPrime;    // cross
                    a = fixedAxisLength;
                    b = w_2.Length();
                }
                // now finish solving the ellipse
                periapsis.x = centroid.x + (iPrime * a).x;
                periapsis.y = centroid.y + (iPrime * a).y;
                e = sqrt(1 - ((b * b) / (a * a)));
                ratio = sqrt(1 - (e*e));
                ae = a * e;
                apseHat = (periapsis - centroid);
                apseHat.Normalize();
                f = apseHat;
                f.Scale(ae);
                f = centroid + f;
                fPrime = apseHat;
                fPrime.Scale(-1 * ae);
                fPrime = centroid + fPrime;
                apoapsis = apseHat;
                apoapsis.Scale(-1 * a);
                apoapsis = centroid + apoapsis;
                phi = atan2(apseHat.y, apseHat.x);
                num = a * (1 - (e * e));
            }
        }
    }


    /**
     * @brief Computes a sequence of 2D vectors to approximate the ellipse
     */
    void approximateEllipse()
    {
        // We will approximate the ellipse as a sequence of connected chords
        // Number of points per quadrant of the ellipse
        int n = static_cast<int>((editCurve.size() - 1) / 4);

        // We choose points in the perifocal frame then translate them to sketch cartesian.
        // This gives us a better approximation of an ellipse, i.e. more points where the
        // curvature is higher.  If the eccentricity is high, we shift the points a bit towards
        // the semi-minor axis.
        double partitionAngle = (M_PI - atan2(b, ae)) / n;
        double radianShift = 0;
        if (e > 0.8) {radianShift = (partitionAngle / 5) * 4;}
        for (int i=0; i < n; i++) {
            theta = i * partitionAngle;
            if (i > 0) {theta = theta + radianShift;}
            r = num / (1 + (e * cos(theta)));
            // r(pi/2) is semi-latus rectum, if we need it
            pos.x = r*cos(theta+phi);  // phi rotates, sin/cos translate
            pos.y = r*sin(theta+phi);
            pos = pos + f;
            posPrime.x = r*cos(theta+phi+M_PI);
            posPrime.y = r*sin(theta+phi+M_PI);
            posPrime = posPrime + fPrime;
            // over the loop, loads Quadrant I points, by using f as origin
            editCurve[i] = pos;
            // over the loop, loads Quadrant III points, by using fPrime as origin
            editCurve[(2*n) + i] = posPrime;
            // load points with negative theta angles (i.e. cw)
            if (i>0) {
                pos.x = r*cos(-1*theta+phi);
                pos.y = r*sin(-1*theta+phi);
                pos = pos + f;
                // loads Quadrant IV points
                editCurve[(4*n) - i] = pos;
                posPrime.x = r*cos(-1*theta+phi+M_PI);
                posPrime.y = r*sin(-1*theta+phi+M_PI);
                posPrime = posPrime + fPrime;
                // loads Quadrant II points
                editCurve[(2*n) - i] = posPrime;
            }
        }
        // load pos & neg b points
        theta = M_PI - atan2(b, ae);        // the angle from f to the positive b point
        r = num / (1 + (e * cos(theta)));
        pos.x = r*cos(theta+phi);
        pos.y = r*sin(theta+phi);
        pos = pos + f;
        editCurve[n] = pos; // positive
        pos.x = r*cos(-1*theta+phi);
        pos.y = r*sin(-1*theta+phi);
        pos = pos + f;
        editCurve[(3*n)] = pos; // negative
        // force the curve to be a closed shape
        editCurve[(4*n)] = editCurve[0];
    }

    /**
     * @brief Prints the ellipse data to STDOUT as an GNU Octave script
     * @param onSketchPos position of the cursor on the sketch
     */
    void ellipseToOctave(Base::Vector2d /*onSketchPos*/)
    {
        int n = static_cast<int>((editCurve.size() - 1) / 4);

        // send a GNU Octave script to stdout to plot points for debugging
        std::ostringstream octave;
        octave << std::fixed << std::setprecision(12);
        octave << "\nclear all;\nclose all;\nclc;\n\n";
        octave << "periapsis = [" << periapsis.x << ", " << periapsis.y << "];\n";
        octave << "apoapsis = [" << apoapsis.x << ", " << apoapsis.y << "];\n";
        octave << "positiveB = [" << editCurve[n].x << ", " << editCurve[n].y << "];\n";
        octave << "apseHat = [" << apseHat.x << ", " << apseHat.y << "];\n";
        octave << "a = " << a << ";\n";
        octave << "b = " << b << ";\n";
        octave << "eccentricity = " << e << ";\n";
        octave << "centroid = [" << centroid.x << ", " << centroid.y << "];\n";
        octave << "f = [" << f.x << ", " << f.y << "];\n";
        octave << "fPrime = [" << fPrime.x << ", " << fPrime.y << "];\n";
        octave << "phi = " << phi << ";\n\n";
        octave << "x = [";
        for (int i=0; i < 4*n + 1; i++) {
            octave << editCurve[i].x;
            if (i < 4*n) {
                octave << ", ";
            }
        }
        octave << "];\n";
        octave << "y = [";
        for (int i=0; i < 4*n + 1; i++) {
            octave << editCurve[i].y;
            if (i < 4*n) {
                octave << ", ";
            }
        }
        octave << "];\n\n";
        octave << "% Draw ellipse points in red;\n";
        octave << "plot (x, y, \"r.\", \"markersize\", 5);\n";
        octave << "axis ([-300, 300, -300, 300], \"square\");grid on;\n";
        octave << "hold on;\n\n";
        octave << "% Draw centroid in blue, f in cyan, and fPrime in magenta;\n";
        octave << "plot(centroid(1), centroid(2), \"b.\", \"markersize\", 5);\n";
        octave << "plot(f(1), f(2), \"c.\", \"markersize\", 5);\n";
        octave << "plot(fPrime(1), fPrime(2), \"m.\", \"markersize\", 5);\n";
        octave << "n = [periapsis(1) - f(1), periapsis(2) - f(2)];\n";
        octave << "h = quiver(f(1),f(2),n(1),n(2), 0);\n";
        octave << "set (h, \"maxheadsize\", 0.1);\n\n";
        octave << "% Draw the three position vectors used for Gui::Command::doCommand(...)\n";
        octave << "periapsisVec = quiver(0,0,periapsis(1),periapsis(2), 0);\n";
        octave << "set (periapsisVec, \"maxheadsize\", 0.01, \"color\", \"black\");\n";
        octave << "centroidVec = quiver(0,0,centroid(1),centroid(2), 0);\n";
        octave << "set (centroidVec, \"maxheadsize\", 0.01, \"color\", \"black\");\n";
        octave << "bVec = quiver(0,0,positiveB(1),positiveB(2), 0);\n";
        octave << "set (bVec, \"maxheadsize\", 0.01, \"color\", \"black\");\n\n";
        octave << "% Draw the local x & y basis vectors, scaled to a and b, in red and blue, respectively\n";
        octave << "xLocalVec = quiver(centroid(1),centroid(2),periapsis(1)-centroid(1),periapsis(2)-centroid(2), 0);\n";
        octave << "set (xLocalVec, \"maxheadsize\", 0.01, \"color\", \"red\");\n";
        octave << "yLocalVec = quiver(centroid(1),centroid(2), positiveB(1)-centroid(1), positiveB(2)-centroid(2), 0);\n";
        octave << "set (yLocalVec, \"maxheadsize\", 0.01, \"color\", \"blue\");\nhold off;\n";
        qDebug() << QString::fromStdString(octave.str());
    }

    /**
     * @brief Finalizes and saves the drawn ellipse
     * @return nothing
     */
    void saveEllipse()
    {
        unsetCursor();
        resetPositionText();

        /* There are a couple of issues with Gui::Command::doCommand(...) and
         * GC_MakeEllipse(...) that cause bugs if not handled properly, even
         * when we give them a mathematically-correct ellipse.
         *
         * GC_MakeEllipse may fail with a gce_InvertAxis error for a small
         * circular ellipse when floating point roundoff or representation
         * errors make the b axis slightly larger than the a axis.
         *
         * A similar, larger, issue arises in Gui::Command::doCommand(...) because
         * we cast our double vector components into strings with a fixed
         * precision of six, and then create new doubles from the strings
         * in EllipsePy::PyInit(...).  Thus, by the time we call GC_MakeEllipse(...)
         * in EllipsePy::PyInit(...), our ellipse may not be valid anymore
         * because b is now greater than a.
         *
         * To handle these issues, we simulate the effects Gui::Command::doCommand(...)
         * has on our ellipse, and we adjust our ellipse parameters until
         * GC_MakeEllipse successfully creates an ellipse with our mangled
         * parameters.
         *
         * In almost all cases, we only have to make our test ellipse one time;
         * it is only in the rare edge cases that require repeated test ellipses
         * until we get a valid one, or fail due to excessive attempts. With a
         * limit of 25 attempts, I have been unable to make it fail.
         */

        // simulate loss of precision in centroid, periapsis, and apoapsis
        char cx[64];
        char cy[64];
        char px[64];
        char py[64];
        char ax[64];
        char ay[64];
        sprintf(cx, "%.6lf\n", centroid.x);
        sprintf(cy, "%.6lf\n", centroid.y);
        sprintf(px, "%.6lf\n", periapsis.x);
        sprintf(py, "%.6lf\n", periapsis.y);
        sprintf(ax, "%.6lf\n", apoapsis.x);
        sprintf(ay, "%.6lf\n", apoapsis.y);
        centroid.x = atof(cx);
        centroid.y = atof(cy);
        periapsis.x = atof(px);
        periapsis.y = atof(py);
        apoapsis.x = atof(ax);
        apoapsis.y = atof(ay);
        double majorLength = (periapsis - apoapsis).Length();
        double minorLength = 0;

        /* GC_MakeEllipse requires a right-handed coordinate system, with +X
         * from centroid to periapsis, +Z out of the page.
         */
        Base::Vector3d k(0,0,1);
        Base::Vector3d i(periapsis.x - centroid.x, periapsis.y - centroid.y, 0);
        Base::Vector3d j = k % i;   // j = k cross i
        double beta = 1e-7;
        int count = 0;
        int limit = 25;             // no infinite loops!
        bool success = false;
        double tempB = b;

        // adjust b until our mangled vectors produce a good ellipse in GC_MakeEllipse
        // and the mangled major and minor lines in LinePy::PyInit(...) are such that
        // major is at least slightly larger than minor
        do {
            tempB = b - double(count * beta);
            j = j.Normalize() * tempB;
            positiveB.x = centroid.x + j.x;
            positiveB.y = centroid.y + j.y;
            negativeB.x = centroid.x + (j.x * -1);
            negativeB.y = centroid.y + (j.y * -1);
            char bpx[64];
            char bpy[64];
            char bnx[64];
            char bny[64];
            sprintf(bpx, "%.6lf\n", positiveB.x);
            sprintf(bpy, "%.6lf\n", positiveB.y);
            sprintf(bnx, "%.6lf\n", negativeB.x);
            sprintf(bny, "%.6lf\n", negativeB.y);
            positiveB.x = atof(bpx);
            positiveB.y = atof(bpy);
            negativeB.x = atof(bnx);
            negativeB.y = atof(bny);
            GC_MakeEllipse me(gp_Pnt(periapsis.x,periapsis.y,0),
                              gp_Pnt(positiveB.x,positiveB.y,0),
                              gp_Pnt(centroid.x,centroid.y,0));
            minorLength = (negativeB - positiveB).Length();
            count++;
            success = me.IsDone() && (minorLength + beta < majorLength);
        } while (!success && (count <= limit));
        if (!success) {
            qDebug() << "Failed to create a valid mangled ellipse after" << count << "attempts";
        }

        // save any changes to b, then recalculate ellipse as required due to change in b
        b = tempB;
        e = sqrt(1 - ((b * b) / (a * a)));
        ae = a * e;
        f = apseHat;
        f.Scale(ae);
        f = centroid + f;
        fPrime = apseHat;
        fPrime.Scale(-1 * ae);
        fPrime = centroid + fPrime;

        int currentgeoid = getHighestCurveIndex(); // index of the ellipse we just created

        try {
            Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Add sketch ellipse"));
            Gui::cmdAppObjectArgs(sketchgui->getObject(), "addGeometry(Part.Ellipse"
                                    "(App.Vector(%f,%f,0),App.Vector(%f,%f,0),App.Vector(%f,%f,0)),%s)",
                                    periapsis.x, periapsis.y,
                                    positiveB.x, positiveB.y,
                                    centroid.x, centroid.y,
                                    geometryCreationMode==Construction?"True":"False");
            Part::GeomEllipse* ellipse =create_elliopse(periapsis, positiveB, centroid);
            static_cast<Sketcher::SketchObject*>(sketchgui->getObject())->addGeometry(ellipse, geometryCreationMode == Construction);
            currentgeoid++;
            Gui::cmdAppObjectArgs(sketchgui->getObject(), "exposeInternalGeometry(%d)", currentgeoid);
            static_cast<Sketcher::SketchObject*>(sketchgui->getObject())->exposeInternalGeometry(currentgeoid);
        }
        catch (const Base::Exception& e) {
            Base::Console().Error("%s\n", e.what());
            Gui::Command::abortCommand();

            tryAutoRecomputeIfNotSolve(static_cast<Sketcher::SketchObject *>(sketchgui->getObject()));

            return;
        }

        Gui::Command::commitCommand();

        if (method == CENTER_PERIAPSIS_B) {
            // add auto constraints for the center point
            if (sugConstr1.size() > 0) {
                createAutoConstraints(sugConstr1, currentgeoid, Sketcher::PointPos::mid);
                sugConstr1.clear();
            }
            if (sugConstr2.size() > 0) {
                createAutoConstraints(sugConstr2, currentgeoid, Sketcher::PointPos::none);
                sugConstr2.clear();
            }
            if (sugConstr3.size() > 0) {
                createAutoConstraints(sugConstr3, currentgeoid, Sketcher::PointPos::none);
                sugConstr3.clear();
            }
        }

        if (method == PERIAPSIS_APOAPSIS_B) {
            if (sugConstr1.size() > 0) {
                createAutoConstraints(sugConstr1, currentgeoid, Sketcher::PointPos::none);
                sugConstr1.clear();
            }
            if (sugConstr2.size() > 0) {
                createAutoConstraints(sugConstr2, currentgeoid, Sketcher::PointPos::none);
                sugConstr2.clear();
            }
            if (sugConstr3.size() > 0) {
                createAutoConstraints(sugConstr3, currentgeoid, Sketcher::PointPos::none);
                sugConstr3.clear();
            }
        }

        tryAutoRecomputeIfNotSolve(static_cast<Sketcher::SketchObject *>(sketchgui->getObject()));

        // This code enables the continuous creation mode.
        if (constrMethod == 0) {
            method = CENTER_PERIAPSIS_B;
            mode = STATUS_SEEK_CENTROID;
        } else {
            method = PERIAPSIS_APOAPSIS_B;
            mode = STATUS_SEEK_PERIAPSIS;
        }
        editCurve.clear();
        drawEdit(editCurve);

        ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Mod/Sketcher");
        bool continuousMode = hGrp->GetBool("ContinuousCreationMode",true);


        if(continuousMode){
            // This code enables the continuous creation mode.
            editCurve.resize(33);
            applyCursor();
            /* It is ok not to call to purgeHandler
            * in continuous creation mode because the
            * handler is destroyed by the quit() method on pressing the
            * right button of the mouse */
        }
        else{
            sketchgui->purgeHandler(); // no code after this line, Handler get deleted in ViewProvider
        }

    }
};

/// @brief Macro that declares a new sketcher command class 'CmdSketcherCreateEllipseByCenter'
DEF_STD_CMD_AU(CmdSketcherCreateEllipseByCenter)

/**
 * @brief ctor
 */
CmdSketcherCreateEllipseByCenter::CmdSketcherCreateEllipseByCenter()
  : Command("Sketcher_CreateEllipseByCenter")
{
    sAppModule      = "Sketcher";
    sGroup          = "Sketcher";
    sMenuText       = QT_TR_NOOP("Create ellipse by center");
    sToolTipText    = QT_TR_NOOP("Create an ellipse by center in the sketch");
    sWhatsThis      = "Sketcher_CreateEllipseByCenter";
    sStatusTip      = sToolTipText;
    sPixmap         = "Sketcher_CreateEllipseByCenter";
    sAccel          = "G, E, E";
    eType           = ForEdit;
}

CONSTRUCTION_UPDATE_ACTION(CmdSketcherCreateEllipseByCenter, "Sketcher_CreateEllipseByCenter")

void CmdSketcherCreateEllipseByCenter::activated(int iMsg)
{
    Q_UNUSED(iMsg);
    ActivateHandler(getActiveGuiDocument(),new DrawSketchHandlerEllipse(0) );
}

bool CmdSketcherCreateEllipseByCenter::isActive(void)
{
    return isCreateGeoActive(getActiveGuiDocument());
}

/// @brief Macro that declares a new sketcher command class 'CmdSketcherCreateEllipseBy3Points'
DEF_STD_CMD_AU(CmdSketcherCreateEllipseBy3Points)

/**
 * @brief ctor
 */
CmdSketcherCreateEllipseBy3Points::CmdSketcherCreateEllipseBy3Points()
  : Command("Sketcher_CreateEllipseBy3Points")
{
    sAppModule      = "Sketcher";
    sGroup          = "Sketcher";
    sMenuText       = QT_TR_NOOP("Create ellipse by 3 points");
    sToolTipText    = QT_TR_NOOP("Create an ellipse by 3 points in the sketch");
    sWhatsThis      = "Sketcher_CreateEllipseBy3Points";
    sStatusTip      = sToolTipText;
    sPixmap         = "Sketcher_CreateEllipse_3points";
    sAccel          = "G, 3, E";
    eType           = ForEdit;
}

CONSTRUCTION_UPDATE_ACTION(CmdSketcherCreateEllipseBy3Points, "Sketcher_CreateEllipse_3points")

void CmdSketcherCreateEllipseBy3Points::activated(int iMsg)
{
    Q_UNUSED(iMsg);
    ActivateHandler(getActiveGuiDocument(),new DrawSketchHandlerEllipse(1) );
}

bool CmdSketcherCreateEllipseBy3Points::isActive(void)
{
    return isCreateGeoActive(getActiveGuiDocument());
}

class DrawSketchHandlerArcOfEllipse : public DrawSketchHandler
{
public:
    DrawSketchHandlerArcOfEllipse()
        : Mode(STATUS_SEEK_First), EditCurve(34)
        , rx(0), ry(0), startAngle(0), endAngle(0)
        , arcAngle(0), arcAngle_t(0)
    {
    }
    virtual ~DrawSketchHandlerArcOfEllipse(){}
    /// mode table
    enum SelectMode {
        STATUS_SEEK_First,      /**< enum value ----. */
        STATUS_SEEK_Second,     /**< enum value ----. */
        STATUS_SEEK_Third,      /**< enum value ----. */
        STATUS_SEEK_Fourth,     /**< enum value ----. */
        STATUS_Close
    };

    virtual void mouseMove(Base::Vector2d onSketchPos) override
    {
        if (Mode==STATUS_SEEK_First) {
            setPositionText(onSketchPos);
            if (seekAutoConstraint(sugConstr1, onSketchPos, Base::Vector2d(0.f,0.f))) { // TODO: ellipse prio 1
                renderSuggestConstraintsCursor(sugConstr1);
                return;
            }
        }
        else if (Mode==STATUS_SEEK_Second) {
            double rx0 = onSketchPos.x - EditCurve[0].x;
            double ry0 = onSketchPos.y - EditCurve[0].y;
            for (int i=0; i < 16; i++) {
                double angle = i*M_PI/16.0;
                double rx1 = rx0 * cos(angle) + ry0 * sin(angle);
                double ry1 = -rx0 * sin(angle) + ry0 * cos(angle);
                EditCurve[1+i] = Base::Vector2d(EditCurve[0].x + rx1, EditCurve[0].y + ry1);
                EditCurve[17+i] = Base::Vector2d(EditCurve[0].x - rx1, EditCurve[0].y - ry1);
            }
            EditCurve[33] = EditCurve[1];

            // Display radius for user
            float radius = (onSketchPos - EditCurve[0]).Length();

            SbString text;
            text.sprintf(" (%.1fR,%.1fR)", radius,radius);
            setPositionText(onSketchPos, text);

            drawEdit(EditCurve);
            if (seekAutoConstraint(sugConstr2, onSketchPos, onSketchPos - centerPoint,
                                   AutoConstraint::CURVE)) {
                renderSuggestConstraintsCursor(sugConstr2);
                return;
            }
        }
        else if (Mode==STATUS_SEEK_Third) {
            // angle between the major axis of the ellipse and the X axis
            double a = (EditCurve[1]-EditCurve[0]).Length();
            double phi = atan2(EditCurve[1].y-EditCurve[0].y,EditCurve[1].x-EditCurve[0].x);

            // This is the angle at cursor point
            double angleatpoint = acos((onSketchPos.x-EditCurve[0].x+(onSketchPos.y-EditCurve[0].y)*tan(phi))/(a*(cos(phi)+tan(phi)*sin(phi))));
            double b=(onSketchPos.y-EditCurve[0].y-a*cos(angleatpoint)*sin(phi))/(sin(angleatpoint)*cos(phi));

            for (int i=1; i < 16; i++) {
                double angle = i*M_PI/16.0;
                double rx1 = a * cos(angle) * cos(phi) - b * sin(angle) * sin(phi);
                double ry1 = a * cos(angle) * sin(phi) + b * sin(angle) * cos(phi);
                EditCurve[1+i] = Base::Vector2d(EditCurve[0].x + rx1, EditCurve[0].y + ry1);
                EditCurve[17+i] = Base::Vector2d(EditCurve[0].x - rx1, EditCurve[0].y - ry1);
            }
            EditCurve[33] = EditCurve[1];
            EditCurve[17] = EditCurve[16];

            // Display radius for user
            SbString text;
            text.sprintf(" (%.1fR,%.1fR)", a, b);
            setPositionText(onSketchPos, text);

            drawEdit(EditCurve);
            if (seekAutoConstraint(sugConstr3, onSketchPos, Base::Vector2d(0.f,0.f))) {
                renderSuggestConstraintsCursor(sugConstr3);
                return;
            }
        }
        else if (Mode==STATUS_SEEK_Fourth) { // here we differ from ellipse creation
            // angle between the major axis of the ellipse and the X axis
            double a = (axisPoint-centerPoint).Length();
            double phi = atan2(axisPoint.y-centerPoint.y,axisPoint.x-centerPoint.x);

            // This is the angle at cursor point
            double angleatpoint = acos((startingPoint.x-centerPoint.x+(startingPoint.y-centerPoint.y)*tan(phi))/(a*(cos(phi)+tan(phi)*sin(phi))));
            double b=abs((startingPoint.y-centerPoint.y-a*cos(angleatpoint)*sin(phi))/(sin(angleatpoint)*cos(phi)));

            double rxs = startingPoint.x - centerPoint.x;
            double rys = startingPoint.y - centerPoint.y;
            startAngle = atan2(a*(rys*cos(phi)-rxs*sin(phi)), b*(rxs*cos(phi)+rys*sin(phi))); // eccentric anomaly angle

            double angle1 = atan2(a*((onSketchPos.y - centerPoint.y)*cos(phi)-(onSketchPos.x - centerPoint.x)*sin(phi)),
                                  b*((onSketchPos.x - centerPoint.x)*cos(phi)+(onSketchPos.y - centerPoint.y)*sin(phi)))- startAngle;

            double angle2 = angle1 + (angle1 < 0. ? 2 : -2) * M_PI ;
            arcAngle = abs(angle1-arcAngle) < abs(angle2-arcAngle) ? angle1 : angle2;

            for (int i=0; i < 34; i++) {
                double angle = startAngle+i*arcAngle/34.0;
                double rx1 = a * cos(angle) * cos(phi) - b * sin(angle) * sin(phi);
                double ry1 = a * cos(angle) * sin(phi) + b * sin(angle) * cos(phi);
                EditCurve[i] = Base::Vector2d(centerPoint.x + rx1, centerPoint.y + ry1);
            }
//             EditCurve[33] = EditCurve[1];
//             EditCurve[17] = EditCurve[16];

            // Display radii and angle for user
            SbString text;
            text.sprintf(" (%.1fR,%.1fR,%.1fdeg)", a, b, arcAngle * 180 / M_PI);
            setPositionText(onSketchPos, text);

            drawEdit(EditCurve);
            if (seekAutoConstraint(sugConstr4, onSketchPos, Base::Vector2d(0.f,0.f))) {
                renderSuggestConstraintsCursor(sugConstr4);
                return;
            }
        }



        applyCursor();
    }

    virtual bool pressButton(Base::Vector2d onSketchPos) override
    {
        if (Mode==STATUS_SEEK_First){
            EditCurve[0] = onSketchPos;
            centerPoint = onSketchPos;
            Mode = STATUS_SEEK_Second;
        }
        else if(Mode==STATUS_SEEK_Second) {
            EditCurve[1] = onSketchPos;
            axisPoint = onSketchPos;
            Mode = STATUS_SEEK_Third;
        }
        else if(Mode==STATUS_SEEK_Third) {
            startingPoint = onSketchPos;
            arcAngle = 0.;
            arcAngle_t= 0.;
            Mode = STATUS_SEEK_Fourth;
        }
        else { // Fourth
            endPoint = onSketchPos;

            Mode = STATUS_Close;
        }
        return true;
    }

    virtual bool releaseButton(Base::Vector2d onSketchPos) override
    {
        Q_UNUSED(onSketchPos);
        if (Mode==STATUS_Close) {
            unsetCursor();
            resetPositionText();

            // angle between the major axis of the ellipse and the X axisEllipse
            double a = (axisPoint-centerPoint).Length();
            double phi = atan2(axisPoint.y-centerPoint.y,axisPoint.x-centerPoint.x);

            // This is the angle at cursor point
            double angleatpoint = acos((startingPoint.x-centerPoint.x+(startingPoint.y-centerPoint.y)*tan(phi))/(a*(cos(phi)+tan(phi)*sin(phi))));
            double b=abs((startingPoint.y-centerPoint.y-a*cos(angleatpoint)*sin(phi))/(sin(angleatpoint)*cos(phi)));

            double angle1 = atan2(a*((endPoint.y - centerPoint.y)*cos(phi)-(endPoint.x - centerPoint.x)*sin(phi)),
                                  b*((endPoint.x - centerPoint.x)*cos(phi)+(endPoint.y - centerPoint.y)*sin(phi)))- startAngle;

            double angle2 = angle1 + (angle1 < 0. ? 2 : -2) * M_PI ;
            arcAngle = abs(angle1-arcAngle) < abs(angle2-arcAngle) ? angle1 : angle2;

            bool isOriginalArcCCW=true;

            if (arcAngle > 0)
                endAngle = startAngle + arcAngle;
            else {
                endAngle = startAngle;
                startAngle += arcAngle;
                isOriginalArcCCW=false;
            }

            Base::Vector2d majAxisDir,minAxisDir,minAxisPoint,majAxisPoint;
            // We always create a CCW ellipse, because we want our XY reference system to be in the +X +Y direction
            // Our normal will then always be in the +Z axis (local +Z axis of the sketcher)

            if(a>b)
            {
                // force second semidiameter to be perpendicular to first semidiamater
                majAxisDir = axisPoint - centerPoint;
                Base::Vector2d perp(-majAxisDir.y,majAxisDir.x);
                perp.Normalize();
                perp.Scale(abs(b));
                minAxisPoint = centerPoint+perp;
                majAxisPoint = centerPoint+majAxisDir;
            }
            else {
                // force second semidiameter to be perpendicular to first semidiamater
                minAxisDir = axisPoint - centerPoint;
                Base::Vector2d perp(minAxisDir.y,-minAxisDir.x);
                perp.Normalize();
                perp.Scale(abs(b));
                majAxisPoint = centerPoint+perp;
                minAxisPoint = centerPoint+minAxisDir;
                endAngle +=  M_PI/2;
                startAngle += M_PI/2;
                phi-=M_PI/2;
                double t=a; a=b; b=t;//swap a,b
            }

            int currentgeoid = getHighestCurveIndex();

            try {
                Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Add sketch arc of ellipse"));

                Gui::cmdAppObjectArgs(sketchgui->getObject(), "addGeometry(Part.ArcOfEllipse"
                    "(Part.Ellipse(App.Vector(%f,%f,0),App.Vector(%f,%f,0),App.Vector(%f,%f,0)),%f,%f),%s)",
                        majAxisPoint.x, majAxisPoint.y,
                        minAxisPoint.x, minAxisPoint.y,
                        centerPoint.x, centerPoint.y,
                        startAngle, endAngle,
                        geometryCreationMode==Construction?"True":"False");
                static_cast<Sketcher::SketchObject*>(sketchgui->getObject())->addGeometry(createArcOfEllipse(majAxisPoint, minAxisPoint, centerPoint, startAngle, endAngle), geometryCreationMode == Construction);
                currentgeoid++;

                Gui::cmdAppObjectArgs(sketchgui->getObject(), "exposeInternalGeometry(%d)", currentgeoid);
                static_cast<Sketcher::SketchObject*>(sketchgui->getObject())->exposeInternalGeometry(currentgeoid);
            }
            catch (const Base::Exception& e) {
                Base::Console().Error("%s\n", e.what());
                Gui::Command::abortCommand();

                tryAutoRecomputeIfNotSolve(static_cast<Sketcher::SketchObject *>(sketchgui->getObject()));

                return false;
            }

            Gui::Command::commitCommand();

            // add auto constraints for the center point
            if (sugConstr1.size() > 0) {
                createAutoConstraints(sugConstr1, currentgeoid, Sketcher::PointPos::mid);
                sugConstr1.clear();
            }

            // add suggested constraints for arc
            if (sugConstr2.size() > 0) {
                createAutoConstraints(sugConstr2, currentgeoid, Sketcher::PointPos::none);
                sugConstr2.clear();
            }

            // add suggested constraints for start of arc
            if (sugConstr3.size() > 0) {
                createAutoConstraints(sugConstr3, currentgeoid, isOriginalArcCCW?Sketcher::PointPos::start:Sketcher::PointPos::end);
                sugConstr3.clear();
            }

            // add suggested constraints for start of arc
            if (sugConstr4.size() > 0) {
                createAutoConstraints(sugConstr4, currentgeoid, isOriginalArcCCW?Sketcher::PointPos::end:Sketcher::PointPos::start);
                sugConstr4.clear();
            }

            tryAutoRecomputeIfNotSolve(static_cast<Sketcher::SketchObject *>(sketchgui->getObject()));

            ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Mod/Sketcher");
            bool continuousMode = hGrp->GetBool("ContinuousCreationMode",true);
            if(continuousMode){
                // This code enables the continuous creation mode.
                Mode=STATUS_SEEK_First;
                EditCurve.clear();
                drawEdit(EditCurve);
                EditCurve.resize(34);
                applyCursor();
                /* this is ok not to call to purgeHandler
                * in continuous creation mode because the
                * handler is destroyed by the quit() method on pressing the
                * right button of the mouse */
            }
            else{
                sketchgui->purgeHandler(); // no code after this line, Handler get deleted in ViewProvider
            }
        }
        return true;
    }

private:
    virtual void activated() override
    {
        setCrosshairCursor("Sketcher_Pointer_Create_ArcOfEllipse");
    }

protected:
    SelectMode Mode;
    std::vector<Base::Vector2d> EditCurve;
    Base::Vector2d centerPoint, axisPoint, startingPoint, endPoint;
    double rx, ry, startAngle, endAngle, arcAngle, arcAngle_t;
    std::vector<AutoConstraint> sugConstr1, sugConstr2, sugConstr3, sugConstr4;
};

DEF_STD_CMD_AU(CmdSketcherCreateArcOfEllipse)

CmdSketcherCreateArcOfEllipse::CmdSketcherCreateArcOfEllipse()
  : Command("Sketcher_CreateArcOfEllipse")
{
    sAppModule      = "Sketcher";
    sGroup          = "Sketcher";
    sMenuText       = QT_TR_NOOP("Create an arc of ellipse");
    sToolTipText    = QT_TR_NOOP("Create an arc of ellipse in the sketch");
    sWhatsThis      = "Sketcher_CreateArcOfEllipse";
    sStatusTip      = sToolTipText;
    sPixmap         = "Sketcher_CreateElliptical_Arc";
    sAccel          = "G, E, A";
    eType           = ForEdit;
}

CONSTRUCTION_UPDATE_ACTION(CmdSketcherCreateArcOfEllipse, "Sketcher_CreateElliptical_Arc")

void CmdSketcherCreateArcOfEllipse::activated(int iMsg)
{
    Q_UNUSED(iMsg);
    ActivateHandler(getActiveGuiDocument(),new DrawSketchHandlerArcOfEllipse() );
}

bool CmdSketcherCreateArcOfEllipse::isActive(void)
{
    return isCreateGeoActive(getActiveGuiDocument());
}


class DrawSketchHandlerArcOfHyperbola : public DrawSketchHandler
{
public:
    DrawSketchHandlerArcOfHyperbola()
      : Mode(STATUS_SEEK_First)
      , EditCurve(34)
      , arcAngle(0)
      , arcAngle_t(0)
    {
    }
    virtual ~DrawSketchHandlerArcOfHyperbola(){}
    /// mode table
    enum SelectMode {
        STATUS_SEEK_First,      /**< enum value ----. */
        STATUS_SEEK_Second,     /**< enum value ----. */
        STATUS_SEEK_Third,     /**< enum value ----. */
        STATUS_SEEK_Fourth,     /**< enum value ----. */
        STATUS_Close
    };

    virtual void mouseMove(Base::Vector2d onSketchPos) override
    {
        if (Mode==STATUS_SEEK_First) {
            setPositionText(onSketchPos);
            if (seekAutoConstraint(sugConstr1, onSketchPos, Base::Vector2d(0.f,0.f))) {
                renderSuggestConstraintsCursor(sugConstr1);
                return;
            }
        }
        else if (Mode==STATUS_SEEK_Second) {
            EditCurve[1]= onSketchPos;

            // Display radius for user
            float radius = (onSketchPos - centerPoint).Length();

            SbString text;
            text.sprintf(" (%.1fR,%.1fR)", radius,radius);
            setPositionText(onSketchPos, text);

            drawEdit(EditCurve);
            if (seekAutoConstraint(sugConstr2, onSketchPos, Base::Vector2d(0.f,0.f),
                                   AutoConstraint::CURVE)) {
                renderSuggestConstraintsCursor(sugConstr2);
                return;
            }
        }
        else if (Mode==STATUS_SEEK_Third) {
            // angle between the major axis of the hyperbola and the X axis
            double a = (axisPoint-centerPoint).Length();
            double phi = atan2(axisPoint.y-centerPoint.y,axisPoint.x-centerPoint.x);

            // This is the angle at cursor point
            double angleatpoint = acosh(((onSketchPos.x-centerPoint.x)*cos(phi)+(onSketchPos.y-centerPoint.y)*sin(phi))/a);
            double b=(onSketchPos.y-centerPoint.y-a*cosh(angleatpoint)*sin(phi))/(sinh(angleatpoint)*cos(phi));

            if(!boost::math::isnan(b)){
                for (int i=15; i >= -15; i--) {
                    // P(U) = O + MajRad*Cosh(U)*XDir + MinRad*Sinh(U)*YDir
                    //double angle = i*M_PI/16.0;
                    double angle=i*angleatpoint/15;
                    double rx = a * cosh(angle) * cos(phi) - b * sinh(angle) * sin(phi);
                    double ry = a * cosh(angle) * sin(phi) + b * sinh(angle) * cos(phi);
                    EditCurve[15+i] = Base::Vector2d(centerPoint.x + rx, centerPoint.y + ry);
                }

                // Display radius for user
                SbString text;
                text.sprintf(" (%.1fR,%.1fR)", a, b);
                setPositionText(onSketchPos, text);
            }

            drawEdit(EditCurve);
            if (seekAutoConstraint(sugConstr3, onSketchPos, Base::Vector2d(0.f,0.f))) {
                renderSuggestConstraintsCursor(sugConstr3);
                return;
            }
        }
        else if (Mode==STATUS_SEEK_Fourth) {
            // angle between the major axis of the hyperbola and the X axis
            double a = (axisPoint-centerPoint).Length();
            double phi = atan2(axisPoint.y-centerPoint.y,axisPoint.x-centerPoint.x);

            // This is the angle at cursor point
            double angleatstartingpoint = acosh(((startingPoint.x-centerPoint.x)*cos(phi)+(startingPoint.y-centerPoint.y)*sin(phi))/a);
            double b=(startingPoint.y-centerPoint.y-a*cosh(angleatstartingpoint)*sin(phi))/(sinh(angleatstartingpoint)*cos(phi));

            double startAngle = angleatstartingpoint;

            //double angleatpoint = acosh(((onSketchPos.x-centerPoint.x)*cos(phi)+(onSketchPos.y-centerPoint.y)*sin(phi))/a);

            double angleatpoint = atanh( (((onSketchPos.y-centerPoint.y)*cos(phi)-(onSketchPos.x-centerPoint.x)*sin(phi))*a) /
                                         (((onSketchPos.x-centerPoint.x)*cos(phi)+(onSketchPos.y-centerPoint.y)*sin(phi))*b)  );

            /*double angle1 = angleatpoint - startAngle;

            double angle2 = angle1 + (angle1 < 0. ? 2 : -2) * M_PI ;
            arcAngle = abs(angle1-arcAngle) < abs(angle2-arcAngle) ? angle1 : angle2;*/

            arcAngle = angleatpoint - startAngle;

            //if(!boost::math::isnan(angle1) && !boost::math::isnan(angle2)){
            if (!boost::math::isnan(arcAngle)) {
                EditCurve.resize(33);
                for (int i=0; i < 33; i++) {
                    // P(U) = O + MajRad*Cosh(U)*XDir + MinRad*Sinh(U)*YDir
                    //double angle=i*angleatpoint/16;
                    double angle = startAngle+i*arcAngle/32.0;
                    double rx = a * cosh(angle) * cos(phi) - b * sinh(angle) * sin(phi);
                    double ry = a * cosh(angle) * sin(phi) + b * sinh(angle) * cos(phi);
                    EditCurve[i] = Base::Vector2d(centerPoint.x + rx, centerPoint.y + ry);
                }

                // Display radius for user
                SbString text;
                text.sprintf(" (%.1fR,%.1fR)", a, b);
                setPositionText(onSketchPos, text);
            }
            else {
                arcAngle=0.;
            }

            drawEdit(EditCurve);
            if (seekAutoConstraint(sugConstr4, onSketchPos, Base::Vector2d(0.f,0.f))) {
                renderSuggestConstraintsCursor(sugConstr4);
                return;
            }
        }

        applyCursor();
    }

    virtual bool pressButton(Base::Vector2d onSketchPos) override
    {
        if (Mode==STATUS_SEEK_First){
            EditCurve[0] = onSketchPos;
            centerPoint = onSketchPos;
            EditCurve.resize(2);
            Mode = STATUS_SEEK_Second;
        }
        else if(Mode==STATUS_SEEK_Second) {
            EditCurve[1] = onSketchPos;
            axisPoint = onSketchPos;
            EditCurve.resize(31);
            Mode = STATUS_SEEK_Third;
        }
        else if(Mode==STATUS_SEEK_Third) {
            startingPoint = onSketchPos;
            arcAngle = 0.;
            arcAngle_t= 0.;
            Mode = STATUS_SEEK_Fourth;
        }
        else { // Fourth
            endPoint = onSketchPos;

            Mode = STATUS_Close;
        }
        return true;
    }

    virtual bool releaseButton(Base::Vector2d /*onSketchPos*/) override
    {
        if (Mode==STATUS_Close) {
            unsetCursor();
            resetPositionText();


            // angle between the major axis of the hyperbola and the X axis
            double a = (axisPoint-centerPoint).Length();
            double phi = atan2(axisPoint.y-centerPoint.y,axisPoint.x-centerPoint.x);

            // This is the angle at cursor point
            double angleatstartingpoint = acosh(((startingPoint.x-centerPoint.x)*cos(phi)+(startingPoint.y-centerPoint.y)*sin(phi))/a);
            double b=(startingPoint.y-centerPoint.y-a*cosh(angleatstartingpoint)*sin(phi))/(sinh(angleatstartingpoint)*cos(phi));

            double startAngle = angleatstartingpoint;

            //double angleatpoint = acosh(((onSketchPos.x-centerPoint.x)*cos(phi)+(onSketchPos.y-centerPoint.y)*sin(phi))/a);

            double endAngle = atanh( (((endPoint.y-centerPoint.y)*cos(phi)-(endPoint.x-centerPoint.x)*sin(phi))*a) /
                                         (((endPoint.x-centerPoint.x)*cos(phi)+(endPoint.y-centerPoint.y)*sin(phi))*b)  );

            if (boost::math::isnan(startAngle) || boost::math::isnan(endAngle)) {
                sketchgui->purgeHandler();
                Base::Console().Error("Cannot create arc of hyperbola from invalid angles, try again!\n");
                return false;
            }


            bool isOriginalArcCCW=true;

            if (arcAngle > 0)
                endAngle = startAngle + arcAngle;
            else {
                endAngle = startAngle;
                startAngle += arcAngle;
                isOriginalArcCCW=false;
            }

            Base::Vector2d majAxisDir,minAxisDir,minAxisPoint,majAxisPoint;
            // We always create a CCW hyperbola, because we want our XY reference system to be in the +X +Y direction
            // Our normal will then always be in the +Z axis (local +Z axis of the sketcher)

            if(a>b)
            {
                // force second semidiameter to be perpendicular to first semidiamater
                majAxisDir = axisPoint - centerPoint;
                Base::Vector2d perp(-majAxisDir.y,majAxisDir.x);
                perp.Normalize();
                perp.Scale(abs(b));
                minAxisPoint = centerPoint+perp;
                majAxisPoint = centerPoint+majAxisDir;
            }
            else {
                // force second semidiameter to be perpendicular to first semidiamater
                minAxisDir = axisPoint - centerPoint;
                Base::Vector2d perp(minAxisDir.y,-minAxisDir.x);
                perp.Normalize();
                perp.Scale(abs(b));
                majAxisPoint = centerPoint+perp;
                minAxisPoint = centerPoint+minAxisDir;
                endAngle +=  M_PI/2;
                startAngle += M_PI/2;
            }

            int currentgeoid = getHighestCurveIndex();

            try {

                Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Add sketch arc of hyperbola"));

                //Add arc of hyperbola, point and constrain point as focus2. We add focus2 for it to balance
                //the intrinsic focus1, in order to balance out the intrinsic invisible focus1 when AOE is
                //dragged by its center
                Gui::cmdAppObjectArgs(sketchgui->getObject(), "addGeometry(Part.ArcOfHyperbola"
                    "(Part.Hyperbola(App.Vector(%f,%f,0),App.Vector(%f,%f,0),App.Vector(%f,%f,0)),%f,%f),%s)",
                    majAxisPoint.x, majAxisPoint.y,
                    minAxisPoint.x, minAxisPoint.y,
                    centerPoint.x, centerPoint.y,
                    startAngle, endAngle,
                    geometryCreationMode==Construction?"True":"False");
                static_cast<Sketcher::SketchObject*>(sketchgui->getObject())->addGeometry(createArcOfHyperbola(majAxisPoint, minAxisPoint, centerPoint, startAngle, endAngle), geometryCreationMode == Construction);
                currentgeoid++;

                Gui::cmdAppObjectArgs(sketchgui->getObject(), "exposeInternalGeometry(%d)", currentgeoid);
                static_cast<Sketcher::SketchObject*>(sketchgui->getObject())->exposeInternalGeometry(currentgeoid);
            }
            catch (const Base::Exception& e) {
                Base::Console().Error("%s\n", e.what());
                Gui::Command::abortCommand();

                tryAutoRecomputeIfNotSolve(static_cast<Sketcher::SketchObject *>(sketchgui->getObject()));

                return false;
            }

            Gui::Command::commitCommand();

            // add auto constraints for the center point
            if (sugConstr1.size() > 0) {
                createAutoConstraints(sugConstr1, currentgeoid, Sketcher::PointPos::mid);
                sugConstr1.clear();
            }

            // add suggested constraints for arc
            if (sugConstr2.size() > 0) {
                createAutoConstraints(sugConstr2, currentgeoid, Sketcher::PointPos::none);
                sugConstr2.clear();
            }

            // add suggested constraints for start of arc
            if (sugConstr3.size() > 0) {
                createAutoConstraints(sugConstr3, currentgeoid, isOriginalArcCCW?Sketcher::PointPos::start:Sketcher::PointPos::end);
                sugConstr3.clear();
            }

            // add suggested constraints for start of arc
            if (sugConstr4.size() > 0) {
                createAutoConstraints(sugConstr4, currentgeoid, isOriginalArcCCW?Sketcher::PointPos::end:Sketcher::PointPos::start);
                sugConstr4.clear();
            }

            tryAutoRecomputeIfNotSolve(static_cast<Sketcher::SketchObject *>(sketchgui->getObject()));

            ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Mod/Sketcher");
            bool continuousMode = hGrp->GetBool("ContinuousCreationMode",true);

            if(continuousMode){
                // This code enables the continuous creation mode.
                Mode = STATUS_SEEK_First;
                EditCurve.clear();
                drawEdit(EditCurve);
                EditCurve.resize(34);
                applyCursor();
                /* It is ok not to call to purgeHandler
                 * in continuous creation mode because the
                 * handler is destroyed by the quit() method on pressing the
                 * right button of the mouse */
            }
            else{
                sketchgui->purgeHandler(); // no code after this line, Handler get deleted in ViewProvider
            }
        }
        return true;
    }

private:
    virtual void activated() override
    {
        setCrosshairCursor("Sketcher_Pointer_Create_ArcOfHyperbola");
    }

protected:
    SelectMode Mode;
    std::vector<Base::Vector2d> EditCurve;
    Base::Vector2d centerPoint, axisPoint, startingPoint, endPoint;
    double arcAngle, arcAngle_t;
    std::vector<AutoConstraint> sugConstr1, sugConstr2, sugConstr3, sugConstr4;

};

DEF_STD_CMD_AU(CmdSketcherCreateArcOfHyperbola)

CmdSketcherCreateArcOfHyperbola::CmdSketcherCreateArcOfHyperbola()
  : Command("Sketcher_CreateArcOfHyperbola")
{
    sAppModule      = "Sketcher";
    sGroup          = "Sketcher";
    sMenuText       = QT_TR_NOOP("Create an arc of hyperbola");
    sToolTipText    = QT_TR_NOOP("Create an arc of hyperbola in the sketch");
    sWhatsThis      = "Sketcher_CreateArcOfHyperbola";
    sStatusTip      = sToolTipText;
    sPixmap         = "Sketcher_CreateHyperbolic_Arc";
    sAccel          = "G, H";
    eType           = ForEdit;
}

CONSTRUCTION_UPDATE_ACTION(CmdSketcherCreateArcOfHyperbola, "Sketcher_CreateHyperbolic_Arc")

void CmdSketcherCreateArcOfHyperbola::activated(int /*iMsg*/)
{
    ActivateHandler(getActiveGuiDocument(),new DrawSketchHandlerArcOfHyperbola() );
}

bool CmdSketcherCreateArcOfHyperbola::isActive(void)
{
    return isCreateGeoActive(getActiveGuiDocument());
}


class DrawSketchHandlerArcOfParabola : public DrawSketchHandler
{
public:
    DrawSketchHandlerArcOfParabola()
        : Mode(STATUS_SEEK_First)
        , EditCurve(34)
        , startAngle(0)
        , endAngle(0)
        , arcAngle(0)
        , arcAngle_t(0)
    {
    }
    virtual ~DrawSketchHandlerArcOfParabola(){}
    /// mode table
    enum SelectMode {
        STATUS_SEEK_First,      /**< enum value ----. */
        STATUS_SEEK_Second,     /**< enum value ----. */
        STATUS_SEEK_Third,      /**< enum value ----. */
        STATUS_SEEK_Fourth,     /**< enum value ----. */
        STATUS_Close
    };

    virtual void mouseMove(Base::Vector2d onSketchPos) override
    {
        if (Mode==STATUS_SEEK_First) {
            setPositionText(onSketchPos);
            if (seekAutoConstraint(sugConstr1, onSketchPos, Base::Vector2d(0.f,0.f))) {
                renderSuggestConstraintsCursor(sugConstr1);
                return;
            }
        }
        else if (Mode==STATUS_SEEK_Second) {
            EditCurve[1]= onSketchPos;

            // Display radius for user
            float radius = (onSketchPos - focusPoint).Length();

            SbString text;
            text.sprintf(" (F%.1f)", radius);
            setPositionText(onSketchPos, text);

            drawEdit(EditCurve);
            if (seekAutoConstraint(sugConstr2, onSketchPos, Base::Vector2d(0.f,0.f))) {
                renderSuggestConstraintsCursor(sugConstr2);
                return;
            }
        }
        else if (Mode==STATUS_SEEK_Third) {
            double focal = (axisPoint-focusPoint).Length();
            double phi = atan2(focusPoint.y-axisPoint.y,focusPoint.x-axisPoint.x);

            // P(U) = O + U*U/(4.*F)*XDir + U*YDir
            //
            // pnt = Base::Vector3d(pnt0.x + angle * angle / 4 / focal * cos(phi) - angle * sin(phi),
            //                      pnt0.y + angle * angle / 4 / focal * sin(phi) + angle * cos(phi),
            //                      0.f);

            // This is the angle at cursor point
            double u =
            ( cos(phi) * (onSketchPos.y - axisPoint.y) - (onSketchPos.x - axisPoint.x) * sin(phi));

            for (int i=15; i >= -15; i--) {
                double angle=i*u/15;
                double rx = angle * angle / 4 / focal * cos(phi) - angle * sin(phi);
                double ry = angle * angle / 4 / focal * sin(phi) + angle * cos(phi);
                EditCurve[15+i] = Base::Vector2d(axisPoint.x + rx, axisPoint.y + ry);
            }

            // Display radius for user
            SbString text;
            text.sprintf(" (F%.1f)", focal);
            setPositionText(onSketchPos, text);

            drawEdit(EditCurve);

            if (seekAutoConstraint(sugConstr3, onSketchPos, Base::Vector2d(0.f,0.f))) {
                renderSuggestConstraintsCursor(sugConstr3);
                return;
            }
        }
        else if (Mode==STATUS_SEEK_Fourth) {
            double focal = (axisPoint-focusPoint).Length();
            double phi = atan2(focusPoint.y-axisPoint.y,focusPoint.x-axisPoint.x);

            // P(U) = O + U*U/(4.*F)*XDir + U*YDir
            //
            // pnt = Base::Vector3d(pnt0.x + angle * angle / 4 / focal * cos(phi) - angle * sin(phi),
            //                      pnt0.y + angle * angle / 4 / focal * sin(phi) + angle * cos(phi),
            //                      0.f);

            // This is the angle at starting point
            double ustartpoint =
            ( cos(phi) * (startingPoint.y - axisPoint.y) - (startingPoint.x - axisPoint.x) * sin(phi));

            double startValue = ustartpoint;

            double u =
            ( cos(phi) * (onSketchPos.y - axisPoint.y) - (onSketchPos.x - axisPoint.x) * sin(phi));


            arcAngle = u - startValue;

            if (!boost::math::isnan(arcAngle)) {
                EditCurve.resize(33);
                for (std::size_t i=0; i < 33; i++) {
                    double angle = startValue+i*arcAngle/32.0;
                    double rx = angle * angle / 4 / focal * cos(phi) - angle * sin(phi);
                    double ry = angle * angle / 4 / focal * sin(phi) + angle * cos(phi);
                    EditCurve[i] = Base::Vector2d(axisPoint.x + rx, axisPoint.y + ry);
                }

                SbString text;
                text.sprintf(" (F%.1f)", focal);
                setPositionText(onSketchPos, text);
            }
            else {
                arcAngle=0.;
            }

            drawEdit(EditCurve);
            if (seekAutoConstraint(sugConstr4, onSketchPos, Base::Vector2d(0.f,0.f))) {
                renderSuggestConstraintsCursor(sugConstr4);
                return;
            }
        }

        applyCursor();
    }

    virtual bool pressButton(Base::Vector2d onSketchPos) override
    {
        if (Mode==STATUS_SEEK_First){
            EditCurve[0] = onSketchPos;
            focusPoint = onSketchPos;
            EditCurve.resize(2);
            Mode = STATUS_SEEK_Second;
        }
        else if(Mode==STATUS_SEEK_Second) {
            EditCurve[1] = onSketchPos;
            axisPoint = onSketchPos;
            EditCurve.resize(31);
            Mode = STATUS_SEEK_Third;
        }
        else if(Mode==STATUS_SEEK_Third) {
            startingPoint = onSketchPos;
            arcAngle = 0.;
            arcAngle_t= 0.;
            Mode = STATUS_SEEK_Fourth;
        }
        else { // Fourth
            endPoint = onSketchPos;
            Mode = STATUS_Close;
        }
        return true;
    }

    virtual bool releaseButton(Base::Vector2d /*onSketchPos*/) override
    {
        if (Mode==STATUS_Close) {
            unsetCursor();
            resetPositionText();

            double phi = atan2(focusPoint.y-axisPoint.y,focusPoint.x-axisPoint.x);

            double ustartpoint =
            ( cos(phi) * (startingPoint.y - axisPoint.y) - (startingPoint.x - axisPoint.x) * sin(phi));

            double uendpoint =
            ( cos(phi) * (endPoint.y - axisPoint.y) - (endPoint.x - axisPoint.x) * sin(phi));

            double startAngle = ustartpoint;

            double endAngle = uendpoint;

            bool isOriginalArcCCW=true;

            if (arcAngle > 0) {
                endAngle = startAngle + arcAngle;
            }
            else {
                endAngle = startAngle;
                startAngle += arcAngle;
                isOriginalArcCCW=false;
            }

            int currentgeoid = getHighestCurveIndex();

            try {
                Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Add sketch arc of Parabola"));

                //Add arc of parabola
                Gui::cmdAppObjectArgs(sketchgui->getObject(), "addGeometry(Part.ArcOfParabola"
                    "(Part.Parabola(App.Vector(%f,%f,0),App.Vector(%f,%f,0),App.Vector(0,0,1)),%f,%f),%s)",
                        focusPoint.x, focusPoint.y,
                        axisPoint.x, axisPoint.y,
                        startAngle, endAngle,
                        geometryCreationMode==Construction?"True":"False");
                static_cast<Sketcher::SketchObject*>(sketchgui->getObject())->addGeometry(createArcOfParabola(focusPoint, axisPoint,startAngle, endAngle), geometryCreationMode == Construction);
                currentgeoid++;

                Gui::cmdAppObjectArgs(sketchgui->getObject(), "exposeInternalGeometry(%d)", currentgeoid);
                static_cast<Sketcher::SketchObject*>(sketchgui->getObject())->exposeInternalGeometry(currentgeoid);
            }
            catch (const Base::Exception& e) {
                Base::Console().Error("%s\n", e.what());
                Gui::Command::abortCommand();

                tryAutoRecomputeIfNotSolve(static_cast<Sketcher::SketchObject *>(sketchgui->getObject()));

                return false;
            }

            Gui::Command::commitCommand();

            // add auto constraints for the focus point
            if (sugConstr1.size() > 0) {
                createAutoConstraints(sugConstr1, currentgeoid+1, Sketcher::PointPos::start);
                sugConstr1.clear();
            }

            // add suggested constraints for vertex point
            if (sugConstr2.size() > 0) {
                createAutoConstraints(sugConstr2, currentgeoid, Sketcher::PointPos::mid);
                sugConstr2.clear();
            }

            // add suggested constraints for start of arc
            if (sugConstr3.size() > 0) {
                createAutoConstraints(sugConstr3, currentgeoid, isOriginalArcCCW?Sketcher::PointPos::start:Sketcher::PointPos::end);
                sugConstr3.clear();
            }

            // add suggested constraints for start of arc
            if (sugConstr4.size() > 0) {
                createAutoConstraints(sugConstr4, currentgeoid, isOriginalArcCCW?Sketcher::PointPos::end:Sketcher::PointPos::start);
                sugConstr4.clear();
            }

            tryAutoRecomputeIfNotSolve(static_cast<Sketcher::SketchObject *>(sketchgui->getObject()));

            ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Mod/Sketcher");
            bool continuousMode = hGrp->GetBool("ContinuousCreationMode",true);
            if (continuousMode) {
                // This code enables the continuous creation mode.
                Mode = STATUS_SEEK_First;
                EditCurve.clear();
                drawEdit(EditCurve);
                EditCurve.resize(34);
                applyCursor();
                /* It is ok not to call to purgeHandler
                 * in continuous creation mode because the
                 * handler is destroyed by the quit() method on pressing the
                 * right button of the mouse */
            }
            else {
                sketchgui->purgeHandler(); // no code after this line, Handler get deleted in ViewProvider
            }
        }
        return true;
    }

private:
    virtual void activated() override
    {
        setCrosshairCursor("Sketcher_Pointer_Create_ArcOfParabola");
    }

protected:
    SelectMode Mode;
    std::vector<Base::Vector2d> EditCurve;
    Base::Vector2d focusPoint, axisPoint, startingPoint, endPoint;
    double startAngle, endAngle, arcAngle, arcAngle_t;
    std::vector<AutoConstraint> sugConstr1, sugConstr2, sugConstr3, sugConstr4;
};

DEF_STD_CMD_AU(CmdSketcherCreateArcOfParabola)

CmdSketcherCreateArcOfParabola::CmdSketcherCreateArcOfParabola()
  : Command("Sketcher_CreateArcOfParabola")
{
    sAppModule      = "Sketcher";
    sGroup          = "Sketcher";
    sMenuText       = QT_TR_NOOP("Create an arc of parabola");
    sToolTipText    = QT_TR_NOOP("Create an arc of parabola in the sketch");
    sWhatsThis      = "Sketcher_CreateArcOfParabola";
    sStatusTip      = sToolTipText;
    sPixmap         = "Sketcher_CreateParabolic_Arc";
    sAccel          = "G, J";
    eType           = ForEdit;
}

CONSTRUCTION_UPDATE_ACTION(CmdSketcherCreateArcOfParabola, "Sketcher_CreateParabolic_Arc")

void CmdSketcherCreateArcOfParabola::activated(int /*iMsg*/)
{
    ActivateHandler(getActiveGuiDocument(),new DrawSketchHandlerArcOfParabola() );
}

bool CmdSketcherCreateArcOfParabola::isActive(void)
{
    return isCreateGeoActive(getActiveGuiDocument());
}


/// @brief Macro that declares a new sketcher command class 'CmdSketcherCompCreateEllipse'
DEF_STD_CMD_ACLU(CmdSketcherCompCreateConic)

/**
 * @brief ctor
 */
CmdSketcherCompCreateConic::CmdSketcherCompCreateConic()
  : Command("Sketcher_CompCreateConic")
{
    sAppModule      = "Sketcher";
    sGroup          = "Sketcher";
    sMenuText       = QT_TR_NOOP("Create a conic");
    sToolTipText    = QT_TR_NOOP("Create a conic in the sketch");
    sWhatsThis      = "Sketcher_CompCreateConic";
    sStatusTip      = sToolTipText;
    eType           = ForEdit;
}

/**
 * @brief Instantiates the conic handler when the conic command activated
 * @param int iMsg
 */
void CmdSketcherCompCreateConic::activated(int iMsg)
{
    if (iMsg == 0) {
        ActivateHandler(getActiveGuiDocument(), new DrawSketchHandlerEllipse(iMsg));
    } else if (iMsg == 1) {
        ActivateHandler(getActiveGuiDocument(), new DrawSketchHandlerEllipse(iMsg));
    } else if (iMsg == 2) {
        ActivateHandler(getActiveGuiDocument(), new DrawSketchHandlerArcOfEllipse());
    } else if (iMsg == 3) {
        ActivateHandler(getActiveGuiDocument(), new DrawSketchHandlerArcOfHyperbola());
    } else if (iMsg == 4) {
        ActivateHandler(getActiveGuiDocument(), new DrawSketchHandlerArcOfParabola());
    } else {
        return;
    }

    // Since the default icon is reset when enabling/disabling the command we have
    // to explicitly set the icon of the used command.
    Gui::ActionGroup* pcAction = dynamic_cast<Gui::ActionGroup*>(_pcAction);
    QList<QAction*> a = pcAction->actions();

    assert(iMsg < a.size());
    pcAction->setIcon(a[iMsg]->icon());
}

Gui::Action * CmdSketcherCompCreateConic::createAction(void)
{
    Gui::ActionGroup* pcAction = new Gui::ActionGroup(this, Gui::getMainWindow());
    pcAction->setDropDownMenu(true);
    applyCommandData(this->className(), pcAction);

    QAction* ellipseByCenter = pcAction->addAction(QString());
    ellipseByCenter->setIcon(Gui::BitmapFactory().iconFromTheme("Sketcher_CreateEllipseByCenter"));
    QAction* ellipseBy3Points = pcAction->addAction(QString());
    ellipseBy3Points->setIcon(Gui::BitmapFactory().iconFromTheme("Sketcher_CreateEllipse_3points"));

    QAction* arcofellipse = pcAction->addAction(QString());
    arcofellipse->setIcon(Gui::BitmapFactory().iconFromTheme("Sketcher_CreateElliptical_Arc"));

    QAction* arcofhyperbola = pcAction->addAction(QString());
    arcofhyperbola->setIcon(Gui::BitmapFactory().iconFromTheme("Sketcher_CreateHyperbolic_Arc"));

    QAction* arcofparabola = pcAction->addAction(QString());
    arcofparabola->setIcon(Gui::BitmapFactory().iconFromTheme("Sketcher_CreateParabolic_Arc"));

    _pcAction = pcAction;
    languageChange();

    // set ellipse by center, a, b as default method
    pcAction->setIcon(Gui::BitmapFactory().iconFromTheme("Sketcher_Conics"));
    int defaultId = 0;
    pcAction->setProperty("defaultAction", QVariant(defaultId));

    return pcAction;
}

void CmdSketcherCompCreateConic::updateAction(int mode)
{
    Gui::ActionGroup* pcAction = dynamic_cast<Gui::ActionGroup*>(getAction());
    if (!pcAction)
        return;

    QList<QAction*> a = pcAction->actions();
    int index=0;// = pcAction->property("defaultAction").toInt();
    switch (mode) {
    case Normal:
        a[0]->setIcon(Gui::BitmapFactory().iconFromTheme("Sketcher_CreateEllipseByCenter"));
        a[1]->setIcon(Gui::BitmapFactory().iconFromTheme("Sketcher_CreateEllipse_3points"));
        a[2]->setIcon(Gui::BitmapFactory().iconFromTheme("Sketcher_CreateElliptical_Arc"));
        a[3]->setIcon(Gui::BitmapFactory().iconFromTheme("Sketcher_CreateHyperbolic_Arc"));
        a[4]->setIcon(Gui::BitmapFactory().iconFromTheme("Sketcher_CreateParabolic_Arc"));
        getAction()->setIcon(a[index]->icon());
        break;
    case Construction:
        a[0]->setIcon(Gui::BitmapFactory().iconFromTheme("Sketcher_CreateEllipseByCenter_Constr"));
        a[1]->setIcon(Gui::BitmapFactory().iconFromTheme("Sketcher_CreateEllipse_3points_Constr"));
        a[2]->setIcon(Gui::BitmapFactory().iconFromTheme("Sketcher_CreateElliptical_Arc_Constr"));
        a[3]->setIcon(Gui::BitmapFactory().iconFromTheme("Sketcher_CreateHyperbolic_Arc_Constr"));
        a[4]->setIcon(Gui::BitmapFactory().iconFromTheme("Sketcher_CreateParabolic_Arc_Constr"));
        getAction()->setIcon(a[index]->icon());
        break;
    }
}

void CmdSketcherCompCreateConic::languageChange()
{
    Command::languageChange();

    if (!_pcAction)
        return;
    Gui::ActionGroup* pcAction = dynamic_cast<Gui::ActionGroup*>(_pcAction);
    QList<QAction*> a = pcAction->actions();

    QAction* ellipseByCenter = a[0];
    ellipseByCenter->setText(QApplication::translate("CmdSketcherCompCreateConic","Ellipse by center, major radius, point"));
    ellipseByCenter->setToolTip(QApplication::translate("Sketcher_CreateEllipseByCenter","Create an ellipse by center, major radius and point"));
    ellipseByCenter->setStatusTip(QApplication::translate("Sketcher_CreateEllipseByCenter","Create an ellipse by center, major radius and point"));
    QAction* ellipseBy3Points = a[1];
    ellipseBy3Points->setText(QApplication::translate("CmdSketcherCompCreateConic","Ellipse by periapsis, apoapsis, minor radius"));
    ellipseBy3Points->setToolTip(QApplication::translate("Sketcher_CreateEllipseBy3Points","Create a ellipse by periapsis, apoapsis, and minor radius"));
    ellipseBy3Points->setStatusTip(QApplication::translate("Sketcher_CreateEllipseBy3Points","Create a ellipse by periapsis, apoapsis, and minor radius"));
    QAction* arcofellipse = a[2];
    arcofellipse->setText(QApplication::translate("CmdSketcherCompCreateConic","Arc of ellipse by center, major radius, endpoints"));
    arcofellipse->setToolTip(QApplication::translate("Sketcher_CreateArcOfEllipse","Create an arc of ellipse by its center, major radius, and endpoints"));
    arcofellipse->setStatusTip(QApplication::translate("Sketcher_CreateArcOfEllipse","Create an arc of ellipse by its center, major radius, and endpoints"));
    QAction* arcofhyperbola = a[3];
    arcofhyperbola->setText(QApplication::translate("CmdSketcherCompCreateConic","Arc of hyperbola by center, major radius, endpoints"));
    arcofhyperbola->setToolTip(QApplication::translate("Sketcher_CreateArcOfHyperbola","Create an arc of hyperbola by its center, major radius, and endpoints"));
    arcofhyperbola->setStatusTip(QApplication::translate("Sketcher_CreateArcOfHyperbola","Create an arc of hyperbola by its center, major radius, and endpoints"));
    QAction* arcofparabola = a[4];
    arcofparabola->setText(QApplication::translate("CmdSketcherCompCreateConic","Arc of parabola by focus, vertex, endpoints"));
    arcofparabola->setToolTip(QApplication::translate("Sketcher_CreateArcOfParabola","Create an arc of parabola by its focus, vertex, and endpoints"));
    arcofparabola->setStatusTip(QApplication::translate("Sketcher_CreateArcOfParabola","Create an arc of parabola by its focus, vertex, and endpoints"));
}

bool CmdSketcherCompCreateConic::isActive(void)
{
    return isCreateGeoActive(getActiveGuiDocument());
}

// ======================================================================================

class DrawSketchHandlerBSpline: public DrawSketchHandler
{
public:
    DrawSketchHandlerBSpline(int constructionMethod)
      : Mode(STATUS_SEEK_FIRST_CONTROLPOINT)
      , MousePressMode(MOUSE_NOT_PRESSED)
      , ConstrMethod(constructionMethod)
      , SplineDegree(3)
      , IsClosed(false)
    {
        addSugConstraint();
        applyCursor();
    }

    virtual ~DrawSketchHandlerBSpline() {}
    /// modes
    enum SELECT_MODE {
        STATUS_SEEK_FIRST_CONTROLPOINT,
        STATUS_SEEK_ADDITIONAL_CONTROLPOINTS,
        STATUS_CLOSE
    };

    // TODO: this kind of behavior will be useful in a superclass
    // when LMB is pressed it's a transitional state so some undos can't be done
    // (like delete last pole)
    enum MOUSE_PRESS_MODE {
        MOUSE_PRESSED,
        MOUSE_NOT_PRESSED
    };

    virtual void mouseMove(Base::Vector2d onSketchPos) override
    {
        prevCursorPosition = onSketchPos;

        if (Mode==STATUS_SEEK_FIRST_CONTROLPOINT) {
            setPositionText(onSketchPos);

            if (seekAutoConstraint(sugConstr.back(), onSketchPos, Base::Vector2d(0.f,0.f))) {
                renderSuggestConstraintsCursor(sugConstr.back());
                return;
            }
        }
        else if (Mode==STATUS_SEEK_ADDITIONAL_CONTROLPOINTS) {

            drawControlPolygonToPosition(onSketchPos);

            drawCursorToPosition(onSketchPos);

            if (seekAutoConstraint(sugConstr.back(), onSketchPos, Base::Vector2d(0.f,0.f))) {
                renderSuggestConstraintsCursor(sugConstr.back());
                return;
            }
        }
    }

    virtual bool pressButton(Base::Vector2d onSketchPos) override
    {
        prevCursorPosition = onSketchPos;

        MousePressMode = MOUSE_PRESSED;

        if (Mode == STATUS_SEEK_FIRST_CONTROLPOINT) {
            BSplinePoles.push_back(onSketchPos);

            Mode = STATUS_SEEK_ADDITIONAL_CONTROLPOINTS;

            // insert circle point for pole, defer internal alignment constraining.
            try {
                Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Add Pole circle"));

                //Add pole
                Gui::cmdAppObjectArgs(sketchgui->getObject(), "addGeometry(Part.Circle(App.Vector(%f,%f,0),App.Vector(0,0,1),10),True)",
                                      BSplinePoles.back().x, BSplinePoles.back().y);
                static_cast<Sketcher::SketchObject*>(sketchgui->getObject())->addGeometry(create_circle(BSplinePoles.back(), 10.0), true);
                poleGeoIds.push_back(getHighestCurveIndex());

                Gui::cmdAppObjectArgs(sketchgui->getObject(), "addConstraint(Sketcher.Constraint('Weight',%d,%f)) ",
                                      poleGeoIds.back(), 1.0 ); // First pole defaults to 1.0 weight
                static_cast<Sketcher::SketchObject*>(sketchgui->getObject())->addConstraintEx(create_constraint(Sketcher::ConstraintType::Weight,
                                      poleGeoIds.back(), 1.0 )); // First pole defaults to 1.0 weight
            }
            catch (const Base::Exception& e) {
                Base::Console().Error("%s\n", e.what());
                Gui::Command::abortCommand();

                static_cast<Sketcher::SketchObject *>(sketchgui->getObject())->solve();

                return false;
            }

            //Gui::Command::commitCommand();

            //static_cast<Sketcher::SketchObject *>(sketchgui->getObject())->solve();

            // add auto constraints on pole
            if (sugConstr.back().size() > 0) {
                createAutoConstraints(sugConstr.back(), poleGeoIds.back(), Sketcher::PointPos::mid, false);
            }

            static_cast<Sketcher::SketchObject *>(sketchgui->getObject())->solve();

            addSugConstraint();

        }
        else if (Mode == STATUS_SEEK_ADDITIONAL_CONTROLPOINTS) {
            BSplinePoles.push_back(onSketchPos);

            // check if coincident with first pole
            for(auto & ac : sugConstr.back()) {
                if( ac.Type == Sketcher::Coincident && ac.GeoId == poleGeoIds[0] && ac.PosId == Sketcher::PointPos::mid ) {
                    IsClosed = true;
                }
            }

            if (IsClosed) {
                Mode = STATUS_CLOSE;

                if (ConstrMethod == 1) { // if periodic we do not need the last pole
                    BSplinePoles.pop_back();
                    sugConstr.pop_back();

                    return true;
                }
            }

            // insert circle point for pole, defer internal alignment constraining.
            try {

                //Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Add Pole circle"));

                //Add pole
                Gui::cmdAppObjectArgs(sketchgui->getObject(), "addGeometry(Part.Circle(App.Vector(%f,%f,0),App.Vector(0,0,1),10),True)",
                                      BSplinePoles.back().x,BSplinePoles.back().y);
                static_cast<Sketcher::SketchObject*>(sketchgui->getObject())->addGeometry(create_circle(BSplinePoles.back(), 10.0), true);
                poleGeoIds.push_back(getHighestCurveIndex());

                Gui::cmdAppObjectArgs(sketchgui->getObject(), "addConstraint(Sketcher.Constraint('Equal',%d,%d)) ",
                                      poleGeoIds[0], poleGeoIds.back());
                static_cast<Sketcher::SketchObject*>(sketchgui->getObject())->addConstraintEx(create_constraint(Sketcher::ConstraintType::Equal,
                                      poleGeoIds[0], poleGeoIds.back()));

            }
            catch (const Base::Exception& e) {
                Base::Console().Error("%s\n", e.what());
                Gui::Command::abortCommand();

                static_cast<Sketcher::SketchObject *>(sketchgui->getObject())->solve();

                return false;
            }

            //Gui::Command::commitCommand();

            //static_cast<Sketcher::SketchObject *>(sketchgui->getObject())->solve();

            // add auto constraints on pole
            if (sugConstr.back().size() > 0) {
                createAutoConstraints(sugConstr.back(), poleGeoIds.back(), Sketcher::PointPos::mid, false);
            }

            //static_cast<Sketcher::SketchObject *>(sketchgui->getObject())->solve();

            if (!IsClosed) {
                addSugConstraint();
            }

        }
        return true;
    }

    virtual bool releaseButton(Base::Vector2d onSketchPos) override
    {
        prevCursorPosition = onSketchPos;
        MousePressMode = MOUSE_NOT_PRESSED;

        return finishCommand(onSketchPos);
    }

    virtual void registerPressedKey(bool pressed, int key) override
    {
        if (SoKeyboardEvent::D == key && pressed) {
            SplineDegree = QInputDialog::getInt(
                Gui::getMainWindow(),
                QObject::tr("B-Spline Degree"),
                QObject::tr("Define B-Spline Degree, between 1 and %1:")
                .arg(QString::number(Geom_BSplineCurve::MaxDegree())),
                SplineDegree, 1, Geom_BSplineCurve::MaxDegree(), 1);
            // FIXME: Pressing Esc here also finishes the B-Spline creation.
            // The user may only want to exit the dialog.
        }
        // On pressing Backspace delete last pole
        else if (SoKeyboardEvent::BACKSPACE == key && pressed) {
            // when mouse is pressed we are in a transitional state so don't mess with it
            if (MOUSE_PRESSED == MousePressMode)
                return;

            // can only delete last pole if it exists
            if (STATUS_SEEK_FIRST_CONTROLPOINT == Mode ||
                STATUS_CLOSE == Mode)
                return;

            // if only first pole exists it's equivalent to canceling current spline
            if (poleGeoIds.size() == 1) {
                // this also exits b-spline creation if continuous mode is off
                this->quit();
                return;
            }

            // reverse the steps of press/release button
            try {
                // already ensured that CurrentConstraint == EditCurve.size() > 1
                const int delGeoId = poleGeoIds.back();
                const auto& constraints = static_cast<Sketcher::SketchObject *>(sketchgui->getObject())->Constraints.getValues();
                for (int i = constraints.size() - 1; i >= 0; --i) {
                    if (delGeoId == constraints[i]->First ||
                        delGeoId == constraints[i]->Second ||
                        delGeoId == constraints[i]->Third)
                        Gui::cmdAppObjectArgs(sketchgui->getObject(), "delConstraint(%d)", i);
                        static_cast<Sketcher::SketchObject*>(sketchgui->getObject())->delConstraint(i);
                }

                // Remove pole
                Gui::cmdAppObjectArgs(sketchgui->getObject(), "delGeometry(%d)", delGeoId);
                static_cast<Sketcher::SketchObject*>(sketchgui->getObject())->delGeometry(delGeoId);
                static_cast<Sketcher::SketchObject *>(sketchgui->getObject())->solve();

                poleGeoIds.pop_back();
                BSplinePoles.pop_back();

                // last entry is kept, as it corresponds to the current pole, but the one corresponding to the erased pole is removed
                sugConstr.erase(std::prev(std::prev(sugConstr.end())));


                // run this in the end to draw lines and position text
                drawControlPolygonToPosition(prevCursorPosition);
                drawCursorToPosition(prevCursorPosition);
            }
            catch (const Base::Exception& e) {
                Base::Console().Error("%s\n", e.what());
                // some commands might have already deleted some constraints/geometries but not others
                Gui::Command::abortCommand();

                static_cast<Sketcher::SketchObject *>(sketchgui->getObject())->solve();

                return;
            }
        }
        // TODO: On pressing, say, W, modify last pole's weight
        // TODO: On pressing, say, M, modify next knot's multiplicity

        return;
    }

    virtual void quit(void) override
    {
        // We must see if we need to create a B-spline before cancelling everything
        // and now just like any other Handler,

        ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Mod/Sketcher");

        bool continuousMode = hGrp->GetBool("ContinuousCreationMode",true);

        if (poleGeoIds.size() > 1) {
            // create B-spline from existing poles
            Mode=STATUS_CLOSE;
            finishCommand(Base::Vector2d(0.f,0.f));
        }
        else if(poleGeoIds.size() == 1) {
            // if we just have one point and we can not close anything, then cancel this creation but continue according to continuous mode
            //sketchgui->getDocument()->undo(1);

            Gui::Command::abortCommand();

            tryAutoRecomputeIfNotSolve(static_cast<Sketcher::SketchObject *>(sketchgui->getObject()));

            if(!continuousMode){
                DrawSketchHandler::quit();
            }
            else {
                // This code disregards existing data and enables the continuous creation mode.
                resetHandlerState();
            }
        }
        else { // we have no data (CurrentConstraint == 0) so user when right-clicking really wants to exit
            DrawSketchHandler::quit();
        }
    }

private:
    void resetHandlerState()
    {
        Mode = STATUS_SEEK_FIRST_CONTROLPOINT;
        applyCursor();

        SplineDegree = 3;

        sugConstr.clear();
        poleGeoIds.clear();
        BSplinePoles.clear();

        eraseEditCurve();

        addSugConstraint();

        IsClosed = false;
    }

    virtual void activated() override
    {
        setCrosshairCursor("Sketcher_Pointer_Create_BSpline");
    }

    void addSugConstraint() {
        std::vector<AutoConstraint> sugConstr1;
        sugConstr.push_back(std::move(sugConstr1));
    }

    void drawControlPolygonToPosition(Base::Vector2d position) {

        std::vector<Base::Vector2d> editcurve(BSplinePoles);
        editcurve.push_back(position);

        drawEdit(editcurve);
    }

    void drawCursorToPosition(Base::Vector2d position) {
        if (!BSplinePoles.empty()) {
            float length = (position - BSplinePoles.back()).Length();
            float angle = (position - BSplinePoles.back()).GetAngle(Base::Vector2d(1.f,0.f));

            SbString text;
            text.sprintf(" (%.1f,%.1fdeg)", length, (angle != -FLOAT_MAX) ? angle * 180 / M_PI : 0);
            setPositionText(position, text);
        }
    }

    void eraseEditCurve() {
        drawEdit(std::vector<Base::Vector2d>());
    }

    bool finishCommand(Base::Vector2d position) {
        if (Mode==STATUS_CLOSE) {
            unsetCursor();
            resetPositionText();

            std::stringstream stream;

            for (auto & pole : BSplinePoles) {
                stream << "App.Vector(" << pole.x << "," << pole.y << "),";
            }

            std::string controlpoints = stream.str();

            // remove last comma and add brackets
            int index = controlpoints.rfind(',');
            controlpoints.resize(index);

            controlpoints.insert(0,1,'[');
            controlpoints.append(1,']');

            int currentgeoid = getHighestCurveIndex();

            try {
                //Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Add B-spline curve"));

                /*Gui::cmdAppObjectArgs(sketchgui->getObject(), "addGeometry(Part.BSplineCurve"
                    "(%s,%s),"
                    "%s)",
                        controlpoints.c_str(),
                        ConstrMethod == 0 ?"False":"True",
                        geometryCreationMode==Construction?"True":"False"); */

                // {"poles", "mults", "knots", "periodic", "degree", "weights", "CheckRational", NULL};
                Gui::cmdAppObjectArgs(sketchgui->getObject(), "addGeometry(Part.BSplineCurve"
                                        "(%s,None,None,%s,%d,None,False),%s)",
                                        controlpoints.c_str(),
                                        ConstrMethod == 0 ?"False":"True",
                                        SplineDegree,
                                        geometryCreationMode==Construction?"True":"False");
                static_cast<Sketcher::SketchObject*>(sketchgui->getObject())->addGeometry(create_bspline(BSplinePoles,ConstrMethod == 0,SplineDegree),geometryCreationMode==Construction);
                currentgeoid++;

                // autoconstraints were added to the circles of the poles, which is ok because they must go to the
                // right position, or the user will freak-out if they appear out of the autoconstrained position.
                // However, autoconstraints on the first and last pole, in normal non-periodic b-splines (with appropriate endpoint knot multiplicity)
                // as the ones created by this tool are intended for the b-spline endpoints, and not for the poles,
                // so here we retrieve any autoconstraint on those poles' center and mangle it to the endpoint.
                if (ConstrMethod == 0) {
                    for(auto & constr : static_cast<Sketcher::SketchObject *>(sketchgui->getObject())->Constraints.getValues()) {
                        if(constr->First == poleGeoIds[0] && constr->FirstPos == Sketcher::PointPos::mid) {
                            constr->First = currentgeoid;
                            constr->FirstPos = Sketcher::PointPos::start;
                        }
                        else if(constr->First == poleGeoIds.back() && constr->FirstPos == Sketcher::PointPos::mid) {
                            constr->First = currentgeoid;
                            constr->FirstPos = Sketcher::PointPos::end;
                        }
                    }
                }

                // Constraint pole circles to B-spline.
                std::stringstream cstream;

                cstream << "conList = []\n";

                for (size_t i = 0; i < poleGeoIds.size(); i++) {
                    cstream << "conList.append(Sketcher.Constraint('InternalAlignment:Sketcher::BSplineControlPoint'," << poleGeoIds[0] + i
                        << "," << static_cast<int>(Sketcher::PointPos::mid) << "," << currentgeoid << "," << i << "))\n";
                }

                cstream << Gui::Command::getObjectCmd(sketchgui->getObject()) << ".addConstraint(conList)\n";
                cstream << "del conList\n";

                Gui::Command::doCommand(Gui::Command::Doc, cstream.str().c_str());

                // for showing the knots on creation
                Gui::cmdAppObjectArgs(sketchgui->getObject(), "exposeInternalGeometry(%d)", currentgeoid);
                static_cast<Sketcher::SketchObject*>(sketchgui->getObject())->exposeInternalGeometry(currentgeoid);
            }
            catch (const Base::Exception& e) {
                Base::Console().Error("%s\n", e.what());
                Gui::Command::abortCommand();

                tryAutoRecomputeIfNotSolve(static_cast<Sketcher::SketchObject *>(sketchgui->getObject()));

                return false;
            }

            Gui::Command::commitCommand();

            tryAutoRecomputeIfNotSolve(static_cast<Sketcher::SketchObject *>(sketchgui->getObject()));

            ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Mod/Sketcher");
            bool continuousMode = hGrp->GetBool("ContinuousCreationMode",true);

            if(continuousMode){
                // This code enables the continuous creation mode.
                resetHandlerState();

                drawCursorToPosition(position);

                /* It is ok not to call to purgeHandler
                 * in continuous creation mode because the
                 * handler is destroyed by the quit() method on pressing the
                 * right button of the mouse */
            }
            else{
                sketchgui->purgeHandler(); // no code after this line, Handler get deleted in ViewProvider
            }
        }
        else {
            drawCursorToPosition(position);
        }

        return true;
    }

protected:
    SELECT_MODE Mode;
    MOUSE_PRESS_MODE MousePressMode;

    // Stores position of the poles of the BSpline.
    std::vector<Base::Vector2d> BSplinePoles;

    // suggested autoconstraints for poles.
    // A new one must be added e.g. using addSugConstraint() before adding a new pole.
    std::vector<std::vector<AutoConstraint>> sugConstr;

    int ConstrMethod;
    int SplineDegree;
    bool IsClosed;
    std::vector<int> poleGeoIds;
    Base::Vector2d prevCursorPosition;
};

DEF_STD_CMD_AU(CmdSketcherCreateBSpline)

CmdSketcherCreateBSpline::CmdSketcherCreateBSpline()
  : Command("Sketcher_CreateBSpline")
{
    sAppModule      = "Sketcher";
    sGroup          = "Sketcher";
    sMenuText       = QT_TR_NOOP("Create B-spline");
    sToolTipText    = QT_TR_NOOP("Create a B-spline via control points in the sketch.");
    sWhatsThis      = "Sketcher_CreateBSpline";
    sStatusTip      = sToolTipText;
    sPixmap         = "Sketcher_CreateBSpline";
    sAccel          = "G, B, B";
    eType           = ForEdit;
}

CONSTRUCTION_UPDATE_ACTION(CmdSketcherCreateBSpline, "Sketcher_CreateBSpline")

void CmdSketcherCreateBSpline::activated(int iMsg)
{
    Q_UNUSED(iMsg);
    ActivateHandler(getActiveGuiDocument(),new DrawSketchHandlerBSpline(0) );
}

bool CmdSketcherCreateBSpline::isActive(void)
{
    return isCreateGeoActive(getActiveGuiDocument());
}

/// @brief Macro that declares a new sketcher command class 'CmdSketcherCreateBSpline'
DEF_STD_CMD_AU(CmdSketcherCreatePeriodicBSpline)

/**
 * @brief ctor
 */
CmdSketcherCreatePeriodicBSpline::CmdSketcherCreatePeriodicBSpline()
: Command("Sketcher_CreatePeriodicBSpline")
{
    sAppModule      = "Sketcher";
    sGroup          = "Sketcher";
    sMenuText       = QT_TR_NOOP("Create periodic B-spline");
    sToolTipText    = QT_TR_NOOP("Create a periodic B-spline via control points in the sketch.");
    sWhatsThis      = "Sketcher_CreatePeriodicBSpline";
    sStatusTip      = sToolTipText;
    sPixmap         = "Sketcher_Create_Periodic_BSpline";
    sAccel          = "G, B, P";
    eType           = ForEdit;
}

CONSTRUCTION_UPDATE_ACTION(CmdSketcherCreatePeriodicBSpline, "Sketcher_Create_Periodic_BSpline")

void CmdSketcherCreatePeriodicBSpline::activated(int iMsg)
{
    Q_UNUSED(iMsg);
    ActivateHandler(getActiveGuiDocument(),new DrawSketchHandlerBSpline(1) );
}

bool CmdSketcherCreatePeriodicBSpline::isActive(void)
{
    return isCreateGeoActive(getActiveGuiDocument());
}


/// @brief Macro that declares a new sketcher command class 'CmdSketcherCompCreateBSpline'
DEF_STD_CMD_ACLU(CmdSketcherCompCreateBSpline)

/**
 * @brief ctor
 */
CmdSketcherCompCreateBSpline::CmdSketcherCompCreateBSpline()
: Command("Sketcher_CompCreateBSpline")
{
    sAppModule      = "Sketcher";
    sGroup          = "Sketcher";
    sMenuText       = QT_TR_NOOP("Create a B-spline");
    sToolTipText    = QT_TR_NOOP("Create a B-spline in the sketch");
    sWhatsThis      = "Sketcher_CompCreateBSpline";
    sStatusTip      = sToolTipText;
    eType           = ForEdit;
}

/**
 * @brief Instantiates the B-spline handler when the B-spline command activated
 * @param int iMsg
 */
void CmdSketcherCompCreateBSpline::activated(int iMsg)
{
    if (iMsg == 0) {
        ActivateHandler(getActiveGuiDocument(), new DrawSketchHandlerBSpline(iMsg));
    } else if (iMsg == 1) {
        ActivateHandler(getActiveGuiDocument(), new DrawSketchHandlerBSpline(iMsg));
    } else {
        return;
    }

    // Since the default icon is reset when enabling/disabling the command we have
    // to explicitly set the icon of the used command.
    Gui::ActionGroup* pcAction = dynamic_cast<Gui::ActionGroup*>(_pcAction);
    QList<QAction*> a = pcAction->actions();

    assert(iMsg < a.size());
    pcAction->setIcon(a[iMsg]->icon());
}

Gui::Action * CmdSketcherCompCreateBSpline::createAction(void)
{
    Gui::ActionGroup* pcAction = new Gui::ActionGroup(this, Gui::getMainWindow());
    pcAction->setDropDownMenu(true);
    applyCommandData(this->className(), pcAction);

    QAction* bspline = pcAction->addAction(QString());
    bspline->setIcon(Gui::BitmapFactory().iconFromTheme("Sketcher_CreateBSpline"));

    QAction* periodicbspline = pcAction->addAction(QString());
    periodicbspline->setIcon(Gui::BitmapFactory().iconFromTheme("Sketcher_Create_Periodic_BSpline"));

    _pcAction = pcAction;
    languageChange();

    // default
    pcAction->setIcon(Gui::BitmapFactory().iconFromTheme("Sketcher_CreateBSpline"));
    int defaultId = 0;
    pcAction->setProperty("defaultAction", QVariant(defaultId));

    return pcAction;
}

void CmdSketcherCompCreateBSpline::updateAction(int mode)
{
    Gui::ActionGroup* pcAction = dynamic_cast<Gui::ActionGroup*>(getAction());
    if (!pcAction)
        return;

    QList<QAction*> a = pcAction->actions();
    int index = 0;// = pcAction->property("defaultAction").toInt();
    switch (mode) {
        case Normal:
            a[0]->setIcon(Gui::BitmapFactory().iconFromTheme("Sketcher_CreateBSpline"));
            a[1]->setIcon(Gui::BitmapFactory().iconFromTheme("Sketcher_Create_Periodic_BSpline"));
            getAction()->setIcon(a[index]->icon());
            break;
        case Construction:
            a[0]->setIcon(Gui::BitmapFactory().iconFromTheme("Sketcher_CreateBSpline_Constr"));
            a[1]->setIcon(Gui::BitmapFactory().iconFromTheme("Sketcher_Create_Periodic_BSpline_Constr"));
            getAction()->setIcon(a[index]->icon());
            break;
    }
}

void CmdSketcherCompCreateBSpline::languageChange()
{
    Command::languageChange();

    if (!_pcAction)
        return;
    Gui::ActionGroup* pcAction = dynamic_cast<Gui::ActionGroup*>(_pcAction);
    QList<QAction*> a = pcAction->actions();

    QAction* bspline = a[0];
    bspline->setText(QApplication::translate("Sketcher_CreateBSpline","B-spline by control points"));
    bspline->setToolTip(QApplication::translate("Sketcher_CreateBSpline","Create a B-spline by control points"));
    bspline->setStatusTip(QApplication::translate("Sketcher_CreateBSpline","Create a B-spline by control points"));
    QAction* periodicbspline = a[1];
    periodicbspline->setText(QApplication::translate("Sketcher_Create_Periodic_BSpline","Periodic B-spline by control points"));
    periodicbspline->setToolTip(QApplication::translate("Sketcher_Create_Periodic_BSpline","Create a periodic B-spline by control points"));
    periodicbspline->setStatusTip(QApplication::translate("Sketcher_Create_Periodic_BSpline","Create a periodic B-spline by control points"));
}

bool CmdSketcherCompCreateBSpline::isActive(void)
{
    return isCreateGeoActive(getActiveGuiDocument());
}


// ======================================================================================

class DrawSketchHandler3PointCircle : public DrawSketchHandler
{
public:
    DrawSketchHandler3PointCircle()
      : Mode(STATUS_SEEK_First),EditCurve(2),radius(1),N(32.0){}
    virtual ~DrawSketchHandler3PointCircle(){}
    /// mode table
    enum SelectMode {
        STATUS_SEEK_First,      /**< enum value ----. */
        STATUS_SEEK_Second,     /**< enum value ----. */
        STATUS_SEEK_Third,      /**< enum value ----. */
        STATUS_End
    };

    virtual void mouseMove(Base::Vector2d onSketchPos) override
    {
        if (Mode == STATUS_SEEK_First) {
            setPositionText(onSketchPos);
            if (seekAutoConstraint(sugConstr1, onSketchPos, Base::Vector2d(0.f,0.f),
                                   AutoConstraint::CURVE)) {
                renderSuggestConstraintsCursor(sugConstr1);
                return;
            }
        }
        else if (Mode == STATUS_SEEK_Second || Mode == STATUS_SEEK_Third) {
            try
            {
                if (Mode == STATUS_SEEK_Second)
                    CenterPoint  = EditCurve[N+1] = (onSketchPos - FirstPoint)/2 + FirstPoint;
                else
                    CenterPoint = EditCurve[N+1] = Part::Geom2dCircle::getCircleCenter(FirstPoint, SecondPoint, onSketchPos);
                radius = (onSketchPos - CenterPoint).Length();
                double lineAngle = GetPointAngle(CenterPoint, onSketchPos);

                // Build a N point circle
                for (int i=1; i < N; i++) {
                    // Start at current angle
                    double angle = i*2*M_PI/N + lineAngle; // N point closed circle has N segments
                    EditCurve[i] = Base::Vector2d(CenterPoint.x + radius*cos(angle),
                                                CenterPoint.y + radius*sin(angle));
                }
                // Beginning and end of curve should be exact
                EditCurve[0] = EditCurve[N] = onSketchPos;

                // Display radius and start angle
                // This lineAngle will report counter-clockwise from +X, not relatively
                SbString text;
                text.sprintf(" (%.1fR,%.1fdeg)", (float) radius, (float) lineAngle * 180 / M_PI);
                setPositionText(onSketchPos, text);

                drawEdit(EditCurve);
                if (Mode == STATUS_SEEK_Second) {
                    if (seekAutoConstraint(sugConstr2, onSketchPos, Base::Vector2d(0.f,0.f),
                                        AutoConstraint::CURVE)) {
                        renderSuggestConstraintsCursor(sugConstr2);
                        return;
                    }
                }
                else {
                    if (seekAutoConstraint(sugConstr3, onSketchPos, Base::Vector2d(0.f,0.f),
                                        AutoConstraint::CURVE)) {
                        renderSuggestConstraintsCursor(sugConstr3);
                        return;
                    }
                }
            }
            catch(Base::ValueError &e) {
                e.ReportException();
            }
        }
        applyCursor();
    }

    virtual bool pressButton(Base::Vector2d onSketchPos) override
    {
        if (Mode == STATUS_SEEK_First) {
            // N point curve + center + endpoint
            EditCurve.resize(N+2);
            FirstPoint = onSketchPos;

            Mode = STATUS_SEEK_Second;
        }
        else if (Mode == STATUS_SEEK_Second) {
            SecondPoint = onSketchPos;

            Mode = STATUS_SEEK_Third;
        }
        else {
            EditCurve.resize(N);

            drawEdit(EditCurve);
            applyCursor();
            Mode = STATUS_End;
        }

        return true;
    }

    virtual bool releaseButton(Base::Vector2d onSketchPos) override
    {
        Q_UNUSED(onSketchPos);
        // Need to look at.  rx might need fixing.
        if (Mode==STATUS_End) {
            unsetCursor();
            resetPositionText();

            try {
                Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Add sketch circle"));
                Gui::cmdAppObjectArgs(sketchgui->getObject(), "addGeometry(Part.Circle"
                    "(App.Vector(%f,%f,0),App.Vector(0,0,1),%f),%s)",
                          CenterPoint.x, CenterPoint.y,
                          radius,
                          geometryCreationMode==Construction?"True":"False");
                const Part::GeomCircle* circle = create_circle(CenterPoint, radius);
                static_cast<Sketcher::SketchObject*>(sketchgui->getObject())->addGeometry(circle, geometryCreationMode == Construction);
                Gui::Command::commitCommand();
            }
            catch (const Base::Exception& e) {
                Base::Console().Error("Failed to add circle: %s\n", e.what());
                Gui::Command::abortCommand();
            }

            // Auto Constraint first picked point
            if (sugConstr1.size() > 0) {
                createAutoConstraints(sugConstr1, getHighestCurveIndex(), Sketcher::PointPos::none);
                sugConstr1.clear();
            }

            // Auto Constraint second picked point
            if (sugConstr2.size() > 0) {
                createAutoConstraints(sugConstr2, getHighestCurveIndex(), Sketcher::PointPos::none);
                sugConstr2.clear();
            }

            // Auto Constraint third picked point
            if (sugConstr3.size() > 0) {
                createAutoConstraints(sugConstr3, getHighestCurveIndex(), Sketcher::PointPos::none);
                sugConstr3.clear();
            }

            tryAutoRecomputeIfNotSolve(static_cast<Sketcher::SketchObject *>(sketchgui->getObject()));

            ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Mod/Sketcher");
            bool continuousMode = hGrp->GetBool("ContinuousCreationMode",true);
            if(continuousMode){
                // This code enables the continuous creation mode.
                Mode=STATUS_SEEK_First;
                EditCurve.clear();
                drawEdit(EditCurve);
                EditCurve.resize(2);
                applyCursor();
                /* this is ok not to call to purgeHandler
                * in continuous creation mode because the
                * handler is destroyed by the quit() method on pressing the
                * right button of the mouse */
            }
            else{
                sketchgui->purgeHandler(); // no code after this line, Handler get deleted in ViewProvider
            }
        }
        return true;
    }

private:
    virtual void activated() override
    {
        setCrosshairCursor("Sketcher_Pointer_Create_3PointCircle");
    }

protected:
    SelectMode Mode;
    std::vector<Base::Vector2d> EditCurve;
    Base::Vector2d CenterPoint, FirstPoint, SecondPoint;
    double radius, N; // N should be even
    std::vector<AutoConstraint> sugConstr1, sugConstr2, sugConstr3;
};

DEF_STD_CMD_AU(CmdSketcherCreate3PointCircle)

CmdSketcherCreate3PointCircle::CmdSketcherCreate3PointCircle()
  : Command("Sketcher_Create3PointCircle")
{
    sAppModule      = "Sketcher";
    sGroup          = "Sketcher";
    sMenuText       = QT_TR_NOOP("Create circle by three points");
    sToolTipText    = QT_TR_NOOP("Create a circle by 3 perimeter points");
    sWhatsThis      = "Sketcher_Create3PointCircle";
    sStatusTip      = sToolTipText;
    sPixmap         = "Sketcher_Create3PointCircle";
    sAccel          = "G, 3, C";
    eType           = ForEdit;
}

CONSTRUCTION_UPDATE_ACTION(CmdSketcherCreate3PointCircle, "Sketcher_Create3PointCircle")

void CmdSketcherCreate3PointCircle::activated(int iMsg)
{
    Q_UNUSED(iMsg);
    ActivateHandler(getActiveGuiDocument(),new DrawSketchHandler3PointCircle() );
}

bool CmdSketcherCreate3PointCircle::isActive(void)
{
    return isCreateGeoActive(getActiveGuiDocument());
}


DEF_STD_CMD_ACLU(CmdSketcherCompCreateCircle)

CmdSketcherCompCreateCircle::CmdSketcherCompCreateCircle()
  : Command("Sketcher_CompCreateCircle")
{
    sAppModule      = "Sketcher";
    sGroup          = "Sketcher";
    sMenuText       = QT_TR_NOOP("Create circle");
    sToolTipText    = QT_TR_NOOP("Create a circle in the sketcher");
    sWhatsThis      = "Sketcher_CompCreateCircle";
    sStatusTip      = sToolTipText;
    eType           = ForEdit;
}

void CmdSketcherCompCreateCircle::activated(int iMsg)
{
    if (iMsg==0)
        ActivateHandler(getActiveGuiDocument(),new DrawSketchHandlerCircle());
    else if (iMsg==1)
        ActivateHandler(getActiveGuiDocument(),new DrawSketchHandler3PointCircle());
    else
        return;

    // Since the default icon is reset when enabling/disabling the command we have
    // to explicitly set the icon of the used command.
    Gui::ActionGroup* pcAction = dynamic_cast<Gui::ActionGroup*>(_pcAction);
    QList<QAction*> a = pcAction->actions();

    assert(iMsg < a.size());
    pcAction->setIcon(a[iMsg]->icon());
}

Gui::Action * CmdSketcherCompCreateCircle::createAction(void)
{
    Gui::ActionGroup* pcAction = new Gui::ActionGroup(this, Gui::getMainWindow());
    pcAction->setDropDownMenu(true);
    applyCommandData(this->className(), pcAction);

    QAction* arc1 = pcAction->addAction(QString());
    arc1->setIcon(Gui::BitmapFactory().iconFromTheme("Sketcher_CreateCircle"));
    QAction* arc2 = pcAction->addAction(QString());
    arc2->setIcon(Gui::BitmapFactory().iconFromTheme("Sketcher_Create3PointCircle"));

    _pcAction = pcAction;
    languageChange();

    pcAction->setIcon(arc1->icon());
    int defaultId = 0;
    pcAction->setProperty("defaultAction", QVariant(defaultId));

    return pcAction;
}

void CmdSketcherCompCreateCircle::updateAction(int mode)
{
    Gui::ActionGroup* pcAction = dynamic_cast<Gui::ActionGroup*>(getAction());
    if (!pcAction)
        return;

    QList<QAction*> a = pcAction->actions();
    int index = 0;// = pcAction->property("defaultAction").toInt();
    switch (mode) {
    case Normal:
        a[0]->setIcon(Gui::BitmapFactory().iconFromTheme("Sketcher_CreateCircle"));
        a[1]->setIcon(Gui::BitmapFactory().iconFromTheme("Sketcher_Create3PointCircle"));
        getAction()->setIcon(a[index]->icon());
        break;
    case Construction:
        a[0]->setIcon(Gui::BitmapFactory().iconFromTheme("Sketcher_CreateCircle_Constr"));
        a[1]->setIcon(Gui::BitmapFactory().iconFromTheme("Sketcher_Create3PointCircle_Constr"));
        getAction()->setIcon(a[index]->icon());
        break;
    }
}

void CmdSketcherCompCreateCircle::languageChange()
{
    Command::languageChange();

    if (!_pcAction)
        return;
    Gui::ActionGroup* pcAction = dynamic_cast<Gui::ActionGroup*>(_pcAction);
    QList<QAction*> a = pcAction->actions();

    QAction* arc1 = a[0];
    arc1->setText(QApplication::translate("CmdSketcherCompCreateCircle", "Center and rim point"));
    arc1->setToolTip(QApplication::translate("Sketcher_CreateCircle", "Create a circle by its center and by a rim point"));
    arc1->setStatusTip(QApplication::translate("Sketcher_CreateCircle", "Create a circle by its center and by a rim point"));
    QAction* arc2 = a[1];
    arc2->setText(QApplication::translate("CmdSketcherCompCreateCircle", "3 rim points"));
    arc2->setToolTip(QApplication::translate("Sketcher_Create3PointCircle", "Create a circle by 3 rim points"));
    arc2->setStatusTip(QApplication::translate("Sketcher_Create3PointCircle", "Create a circle by 3 rim points"));
}

bool CmdSketcherCompCreateCircle::isActive(void)
{
    return isCreateGeoActive(getActiveGuiDocument());
}


// ======================================================================================

class DrawSketchHandlerPoint: public DrawSketchHandler
{
public:
    DrawSketchHandlerPoint() : selectionDone(false) {}
    virtual ~DrawSketchHandlerPoint() {}

    virtual void mouseMove(Base::Vector2d onSketchPos) override
    {
        setPositionText(onSketchPos);
        if (seekAutoConstraint(sugConstr, onSketchPos, Base::Vector2d(0.f,0.f))) {
            renderSuggestConstraintsCursor(sugConstr);
            return;
        }
        applyCursor();
    }

    virtual bool pressButton(Base::Vector2d onSketchPos) override
    {
        EditPoint = onSketchPos;
        selectionDone = true;
        return true;
    }

    virtual bool releaseButton(Base::Vector2d onSketchPos) override
    {
        Q_UNUSED(onSketchPos);
        if (selectionDone){
            unsetCursor();
            resetPositionText();

            try {
                Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Add sketch point"));
                Gui::cmdAppObjectArgs(sketchgui->getObject(), "addGeometry(Part.Point(App.Vector(%f,%f,0)))",
                          EditPoint.x,EditPoint.y);
                static_cast<Sketcher::SketchObject*>(sketchgui->getObject())->addGeometry(create_point(EditPoint.x, EditPoint.y));
                Gui::Command::commitCommand();
            }
            catch (const Base::Exception& e) {
                Base::Console().Error("Failed to add point: %s\n", e.what());
                Gui::Command::abortCommand();
            }

            // add auto constraints for the line segment start
            if (sugConstr.size() > 0) {
                createAutoConstraints(sugConstr, getHighestCurveIndex(), Sketcher::PointPos::start);
                sugConstr.clear();
            }

            tryAutoRecomputeIfNotSolve(static_cast<Sketcher::SketchObject *>(sketchgui->getObject()));

            ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Mod/Sketcher");
            bool continuousMode = hGrp->GetBool("ContinuousCreationMode",true);
            if(continuousMode){
                // This code enables the continuous creation mode.
                applyCursor();
                /* It is ok not to call to purgeHandler
                * in continuous creation mode because the
                * handler is destroyed by the quit() method on pressing the
                * right button of the mouse */
            }
            else{
                sketchgui->purgeHandler(); // no code after this line, Handler get deleted in ViewProvider
            }
        }
        return true;
    }

private:
    virtual void activated() override
    {
        setCrosshairCursor("Sketcher_Pointer_Create_Point");
    }

protected:
    bool selectionDone;
    Base::Vector2d EditPoint;
    std::vector<AutoConstraint> sugConstr;
};

DEF_STD_CMD_A(CmdSketcherCreatePoint)

CmdSketcherCreatePoint::CmdSketcherCreatePoint()
  : Command("Sketcher_CreatePoint")
{
    sAppModule      = "Sketcher";
    sGroup          = "Sketcher";
    sMenuText       = QT_TR_NOOP("Create point");
    sToolTipText    = QT_TR_NOOP("Create a point in the sketch");
    sWhatsThis      = "Sketcher_CreatePoint";
    sStatusTip      = sToolTipText;
    sPixmap         = "Sketcher_CreatePoint";
    sAccel          = "G, Y";
    eType           = ForEdit;
}

void CmdSketcherCreatePoint::activated(int iMsg)
{
    Q_UNUSED(iMsg);
    ActivateHandler(getActiveGuiDocument(), new DrawSketchHandlerPoint());
}

bool CmdSketcherCreatePoint::isActive(void)
{
    return isCreateGeoActive(getActiveGuiDocument());
}


// ======================================================================================

DEF_STD_CMD_A(CmdSketcherCreateText)

CmdSketcherCreateText::CmdSketcherCreateText()
  : Command("Sketcher_CreateText")
{
    sAppModule      = "Sketcher";
    sGroup          = "Sketcher";
    sMenuText       = QT_TR_NOOP("Create text");
    sToolTipText    = QT_TR_NOOP("Create text in the sketch");
    sWhatsThis      = "Sketcher_CreateText";
    sStatusTip      = sToolTipText;
    sPixmap         = "Sketcher_CreateText";
    eType           = ForEdit;
}

void CmdSketcherCreateText::activated(int iMsg)
{
    Q_UNUSED(iMsg);
}

bool CmdSketcherCreateText::isActive(void)
{
    return false;
}


// ======================================================================================

DEF_STD_CMD_A(CmdSketcherCreateDraftLine)

CmdSketcherCreateDraftLine::CmdSketcherCreateDraftLine()
  : Command("Sketcher_CreateDraftLine")
{
    sAppModule      = "Sketcher";
    sGroup          = "Sketcher";
    sMenuText       = QT_TR_NOOP("Create draft line");
    sToolTipText    = QT_TR_NOOP("Create a draft line in the sketch");
    sWhatsThis      = "Sketcher_CreateDraftLine";
    sStatusTip      = sToolTipText;
    sPixmap         = "Sketcher_DraftLine";
    eType           = ForEdit;
}

void CmdSketcherCreateDraftLine::activated(int iMsg)
{
    Q_UNUSED(iMsg);
}

bool CmdSketcherCreateDraftLine::isActive(void)
{
    return false;
}

// ======================================================================================

namespace SketcherGui {
    class FilletSelection : public Gui::SelectionFilterGate
    {
        App::DocumentObject* object;
    public:
        FilletSelection(App::DocumentObject* obj)
            : Gui::SelectionFilterGate(nullPointer()), object(obj)
        {}

        bool allow(App::Document * /*pDoc*/, App::DocumentObject *pObj, const char *sSubName)
        {
            if (pObj != this->object)
                return false;
            if (!sSubName || sSubName[0] == '\0')
                return false;
            std::string element(sSubName);
            if (element.substr(0,4) == "Edge") {
                int GeoId = std::atoi(element.substr(4,4000).c_str()) - 1;
                Sketcher::SketchObject *Sketch = static_cast<Sketcher::SketchObject*>(object);
                const Part::Geometry *geom = Sketch->getGeometry(GeoId);
                if (geom->getTypeId().isDerivedFrom(Part::GeomBoundedCurve::getClassTypeId()))
                    return true;
            }
            if (element.substr(0,6) == "Vertex") {
                int VtId = std::atoi(element.substr(6,4000).c_str()) - 1;
                Sketcher::SketchObject *Sketch = static_cast<Sketcher::SketchObject*>(object);
                std::vector<int> GeoIdList;
                std::vector<Sketcher::PointPos> PosIdList;
                Sketch->getDirectlyCoincidentPoints(VtId, GeoIdList, PosIdList);
                if (GeoIdList.size() == 2 && GeoIdList[0] >= 0  && GeoIdList[1] >= 0) {
                    const Part::Geometry *geom1 = Sketch->getGeometry(GeoIdList[0]);
                    const Part::Geometry *geom2 = Sketch->getGeometry(GeoIdList[1]);
                    if (geom1->getTypeId() == Part::GeomLineSegment::getClassTypeId() &&
                        geom2->getTypeId() == Part::GeomLineSegment::getClassTypeId())
                        return true;
                }
            }
            return  false;
        }
    };
}

class DrawSketchHandlerFillet: public DrawSketchHandler
{
public:
    enum FilletType {
        SimpleFillet,
        ConstraintPreservingFillet
    };

    DrawSketchHandlerFillet(FilletType filletType) : filletType(filletType), Mode(STATUS_SEEK_First), firstCurve(0) {}
    virtual ~DrawSketchHandlerFillet()
    {
        Gui::Selection().rmvSelectionGate();
    }

    enum SelectMode{
        STATUS_SEEK_First,
        STATUS_SEEK_Second
    };

    virtual void mouseMove(Base::Vector2d onSketchPos) override
    {
        Q_UNUSED(onSketchPos);
    }

    virtual bool pressButton(Base::Vector2d onSketchPos) override
    {
        Q_UNUSED(onSketchPos);
        return true;
    }

    virtual bool releaseButton(Base::Vector2d onSketchPos) override
    {
        bool construction=false;
        int VtId = getPreselectPoint();
        if (Mode == STATUS_SEEK_First && VtId != -1) {
            int GeoId;
            Sketcher::PointPos PosId=Sketcher::PointPos::none;
            sketchgui->getSketchObject()->getGeoVertexIndex(VtId,GeoId,PosId);
            const Part::Geometry *geom = sketchgui->getSketchObject()->getGeometry(GeoId);
            if (geom->getTypeId() == Part::GeomLineSegment::getClassTypeId() &&
                (PosId == Sketcher::PointPos::start || PosId == Sketcher::PointPos::end)) {

                // guess fillet radius
                double radius=-1;
                std::vector<int> GeoIdList;
                std::vector<Sketcher::PointPos> PosIdList;
                sketchgui->getSketchObject()->getDirectlyCoincidentPoints(GeoId, PosId, GeoIdList, PosIdList);
                if (GeoIdList.size() == 2 && GeoIdList[0] >= 0  && GeoIdList[1] >= 0) {
                    const Part::Geometry *geom1 = sketchgui->getSketchObject()->getGeometry(GeoIdList[0]);
                    const Part::Geometry *geom2 = sketchgui->getSketchObject()->getGeometry(GeoIdList[1]);
                    construction=Sketcher::GeometryFacade::getConstruction(geom1) && Sketcher::GeometryFacade::getConstruction(geom2);
                    if (geom1->getTypeId() == Part::GeomLineSegment::getClassTypeId() &&
                        geom2->getTypeId() == Part::GeomLineSegment::getClassTypeId()) {
                        const Part::GeomLineSegment *lineSeg1 = static_cast<const Part::GeomLineSegment *>(geom1);
                        const Part::GeomLineSegment *lineSeg2 = static_cast<const Part::GeomLineSegment *>(geom2);
                        Base::Vector3d dir1 = lineSeg1->getEndPoint() - lineSeg1->getStartPoint();
                        Base::Vector3d dir2 = lineSeg2->getEndPoint() - lineSeg2->getStartPoint();
                        if (PosIdList[0] == Sketcher::PointPos::end)
                            dir1 *= -1;
                        if (PosIdList[1] == Sketcher::PointPos::end)
                            dir2 *= -1;
                        double l1 = dir1.Length();
                        double l2 = dir2.Length();
                        double angle = dir1.GetAngle(dir2);
                        radius = (l1 < l2 ? l1 : l2) * 0.2 * sin(angle/2);
                    }
                }
                if (radius < 0)
                    return false;

                int currentgeoid= getHighestCurveIndex();
                // create fillet at point
                try {
                    bool pointFillet = (filletType == 1);
                    Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Create fillet"));
                    Gui::cmdAppObjectArgs(sketchgui->getObject(), "fillet(%d,%d,%f,%s,%s)", GeoId, static_cast<int>(PosId), radius, "True",
                        pointFillet ? "True":"False");

                    if (construction) {
                        Gui::cmdAppObjectArgs(sketchgui->getObject(), "toggleConstruction(%d) ", currentgeoid+1);
                    }

                    Gui::Command::commitCommand();
                }
                catch (const Base::Exception& e) {
                    Base::Console().Error("Failed to create fillet: %s\n", e.what());
                    Gui::Command::abortCommand();
                }

                tryAutoRecomputeIfNotSolve(static_cast<Sketcher::SketchObject *>(sketchgui->getObject()));
            }
            return true;
        }

        int GeoId = getPreselectCurve();
        if (GeoId > -1) {
            const Part::Geometry *geom = sketchgui->getSketchObject()->getGeometry(GeoId);
            if (geom->getTypeId().isDerivedFrom(Part::GeomBoundedCurve::getClassTypeId())) {
                if (Mode==STATUS_SEEK_First) {
                    firstCurve = GeoId;
                    firstPos = onSketchPos;
                    Mode = STATUS_SEEK_Second;
                    // add the line to the selection
                    std::stringstream ss;
                    ss << "Edge" << firstCurve + 1;
                    Gui::Selection().addSelection(sketchgui->getSketchObject()->getDocument()->getName()
                                                 ,sketchgui->getSketchObject()->getNameInDocument()
                                                 ,ss.str().c_str()
                                                 ,onSketchPos.x
                                                 ,onSketchPos.y
                                                 ,0.f);
                }
                else if (Mode==STATUS_SEEK_Second) {
                    int secondCurve = GeoId;
                    Base::Vector2d secondPos = onSketchPos;

                    Base::Vector3d refPnt1(firstPos.x, firstPos.y, 0.f);
                    Base::Vector3d refPnt2(secondPos.x, secondPos.y, 0.f);

                    const Part::Geometry *geom1 = sketchgui->getSketchObject()->getGeometry(firstCurve);

                    double radius = 0;

                    if( geom->getTypeId() == Part::GeomLineSegment::getClassTypeId() &&
                        geom1->getTypeId() == Part::GeomLineSegment::getClassTypeId()) {
                        // guess fillet radius
                        const Part::GeomLineSegment *lineSeg1 = static_cast<const Part::GeomLineSegment *>
                                                                (sketchgui->getSketchObject()->getGeometry(firstCurve));
                        const Part::GeomLineSegment *lineSeg2 = static_cast<const Part::GeomLineSegment *>
                                                                (sketchgui->getSketchObject()->getGeometry(secondCurve));

                        radius = Part::suggestFilletRadius(lineSeg1, lineSeg2, refPnt1, refPnt2);
                        if (radius < 0)
                            return false;

                        construction=Sketcher::GeometryFacade::getConstruction(lineSeg1) && Sketcher::GeometryFacade::getConstruction(lineSeg2);
                    }
                    else { // other supported curves
                        const Part::Geometry *geo1 = static_cast<const Part::Geometry *>
                                                                (sketchgui->getSketchObject()->getGeometry(firstCurve));
                        const Part::Geometry *geo2 = static_cast<const Part::Geometry *>
                                                                (sketchgui->getSketchObject()->getGeometry(secondCurve));

                        construction=Sketcher::GeometryFacade::getConstruction(geo1) && Sketcher::GeometryFacade::getConstruction(geo2);
                    }


                    int currentgeoid= getHighestCurveIndex();

                    // create fillet between lines
                    try {
                        bool pointFillet = (filletType == 1);
                        Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Create fillet"));
                        Gui::cmdAppObjectArgs(sketchgui->getObject(), "fillet(%d,%d,App.Vector(%f,%f,0),App.Vector(%f,%f,0),%f,%s,%s)",
                                  firstCurve, secondCurve,
                                  firstPos.x, firstPos.y,
                                  secondPos.x, secondPos.y, radius,
                                  "True", pointFillet ? "True":"False");
                        Gui::Command::commitCommand();
                    }
                    catch (const Base::CADKernelError& e) {
                        e.ReportException();
                        if(e.getTranslatable()) {
                            QMessageBox::warning(Gui::getMainWindow(), QObject::tr("CAD Kernel Error"),
                                                QObject::tr(e.getMessage().c_str()));
                        }
                        Gui::Selection().clearSelection();
                        Gui::Command::abortCommand();
                        Mode = STATUS_SEEK_First;
                    }
                    catch (const Base::ValueError& e) {
                        e.ReportException();
                        Gui::Selection().clearSelection();
                        Gui::Command::abortCommand();
                        Mode = STATUS_SEEK_First;
                    }

                    tryAutoRecompute(static_cast<Sketcher::SketchObject *>(sketchgui->getObject()));

                    if(construction) {
                        Gui::cmdAppObjectArgs(sketchgui->getObject(), "toggleConstruction(%d) ",
                            currentgeoid+1);
                    }


                    Gui::Selection().clearSelection();
                    Mode = STATUS_SEEK_First;
                }
            }
        }

        if (VtId < 0 && GeoId < 0) // exit the fillet tool if the user clicked on empty space
            sketchgui->purgeHandler(); // no code after this line, Handler get deleted in ViewProvider

        return true;
    }

private:
    virtual void activated() override
    {
        Gui::Selection().rmvSelectionGate();
        Gui::Selection().addSelectionGate(new FilletSelection(sketchgui->getObject()));
        setCrosshairCursor("Sketcher_Pointer_Create_Fillet");
    }

protected:
    int filletType;
    SelectMode Mode;
    int firstCurve;
    Base::Vector2d firstPos;
};

DEF_STD_CMD_A(CmdSketcherCreateFillet)

CmdSketcherCreateFillet::CmdSketcherCreateFillet()
  : Command("Sketcher_CreateFillet")
{
    sAppModule      = "Sketcher";
    sGroup          = "Sketcher";
    sMenuText       = QT_TR_NOOP("Create fillet");
    sToolTipText    = QT_TR_NOOP("Create a fillet between two lines or at a coincident point");
    sWhatsThis      = "Sketcher_CreateFillet";
    sStatusTip      = sToolTipText;
    sPixmap         = "Sketcher_CreateFillet";
    sAccel          = "G, F, F";
    eType           = ForEdit;
}

void CmdSketcherCreateFillet::activated(int iMsg)
{
    Q_UNUSED(iMsg);
    ActivateHandler(getActiveGuiDocument(), new DrawSketchHandlerFillet(DrawSketchHandlerFillet::SimpleFillet));
}

bool CmdSketcherCreateFillet::isActive(void)
{
    return isCreateGeoActive(getActiveGuiDocument());
}

// ======================================================================================

DEF_STD_CMD_A(CmdSketcherCreatePointFillet)

CmdSketcherCreatePointFillet::CmdSketcherCreatePointFillet()
  : Command("Sketcher_CreatePointFillet")
{
    sAppModule      = "Sketcher";
    sGroup          = "Sketcher";
    sMenuText       = QT_TR_NOOP("Create corner-preserving fillet");
    sToolTipText    = QT_TR_NOOP("Fillet that preserves intersection point and most constraints");
    sWhatsThis      = "Sketcher_CreatePointFillet";
    sStatusTip      = sToolTipText;
    sPixmap         = "Sketcher_CreatePointFillet";
    sAccel          = "G, F, P";
    eType           = ForEdit;
}

void CmdSketcherCreatePointFillet::activated(int iMsg)
{
    Q_UNUSED(iMsg);
    ActivateHandler(getActiveGuiDocument(), new DrawSketchHandlerFillet(DrawSketchHandlerFillet::ConstraintPreservingFillet));
}

bool CmdSketcherCreatePointFillet::isActive(void)
{
    return isCreateGeoActive(getActiveGuiDocument());
}

/// @brief Macro that declares a new sketcher command class 'CmdSketcherCompCreateFillets'
DEF_STD_CMD_ACLU(CmdSketcherCompCreateFillets)

/**
 * @brief ctor
 */
CmdSketcherCompCreateFillets::CmdSketcherCompCreateFillets()
  : Command("Sketcher_CompCreateFillets")
{
    sAppModule      = "Sketcher";
    sGroup          = "Sketcher";
    sMenuText       = QT_TR_NOOP("Fillets");
    sToolTipText    = QT_TR_NOOP("Create a fillet between two lines");
    sWhatsThis      = "Sketcher_CompCreateFillets";
    sStatusTip      = sToolTipText;
    eType           = ForEdit;
}

/**
 * @brief Instantiates the fillet handler when the fillet command activated
 * @param int iMsg
 */
void CmdSketcherCompCreateFillets::activated(int iMsg)
{
    if (iMsg == 0) {
        ActivateHandler(getActiveGuiDocument(), new DrawSketchHandlerFillet(DrawSketchHandlerFillet::SimpleFillet));
    } else if (iMsg == 1) {
        ActivateHandler(getActiveGuiDocument(), new DrawSketchHandlerFillet(DrawSketchHandlerFillet::ConstraintPreservingFillet));
    } else {
        return;
    }

    // Since the default icon is reset when enabling/disabling the command we have
    // to explicitly set the icon of the used command.
    Gui::ActionGroup* pcAction = dynamic_cast<Gui::ActionGroup*>(_pcAction);
    QList<QAction*> a = pcAction->actions();

    assert(iMsg < a.size());
    pcAction->setIcon(a[iMsg]->icon());
}

Gui::Action * CmdSketcherCompCreateFillets::createAction(void)
{
    Gui::ActionGroup* pcAction = new Gui::ActionGroup(this, Gui::getMainWindow());
    pcAction->setDropDownMenu(true);
    applyCommandData(this->className(), pcAction);

    QAction* oldFillet = pcAction->addAction(QString());
    oldFillet->setIcon(Gui::BitmapFactory().iconFromTheme("Sketcher_CreateFillet"));

    QAction* pointFillet = pcAction->addAction(QString());
    pointFillet->setIcon(Gui::BitmapFactory().iconFromTheme("Sketcher_CreatePointFillet"));

    _pcAction = pcAction;
    languageChange();

    pcAction->setIcon(Gui::BitmapFactory().iconFromTheme("Sketcher_CreateFillet"));
    int defaultId = 0;
    pcAction->setProperty("defaultAction", QVariant(defaultId));

    return pcAction;
}

void CmdSketcherCompCreateFillets::updateAction(int mode)
{
    Q_UNUSED(mode);
    Gui::ActionGroup* pcAction = dynamic_cast<Gui::ActionGroup*>(getAction());
    if (!pcAction)
        return;

    QList<QAction*> a = pcAction->actions();
    int index = 0;// = pcAction->property("defaultAction").toInt();
    a[0]->setIcon(Gui::BitmapFactory().iconFromTheme("Sketcher_CreateFillet"));
    a[1]->setIcon(Gui::BitmapFactory().iconFromTheme("Sketcher_CreatePointFillet"));
    getAction()->setIcon(a[index]->icon());
}

void CmdSketcherCompCreateFillets::languageChange()
{
    Command::languageChange();

    if (!_pcAction)
        return;
    Gui::ActionGroup* pcAction = dynamic_cast<Gui::ActionGroup*>(_pcAction);
    QList<QAction*> a = pcAction->actions();

    QAction* oldFillet = a[0];
    oldFillet->setText(QApplication::translate("CmdSketcherCompCreateFillets","Sketch fillet"));
    oldFillet->setToolTip(QApplication::translate("Sketcher_CreateFillet","Creates a radius between two lines"));
    oldFillet->setStatusTip(QApplication::translate("Sketcher_CreateFillet","Creates a radius between two lines"));
    QAction* pointFillet = a[1];
    pointFillet->setText(QApplication::translate("CmdSketcherCompCreateFillets","Constraint-preserving sketch fillet"));
    pointFillet->setToolTip(QApplication::translate("Sketcher_CreatePointFillet","Fillet that preserves constraints and intersection point"));
    pointFillet->setStatusTip(QApplication::translate("Sketcher_CreatePointFillet","Fillet that preserves constraints and intersection point"));
}

bool CmdSketcherCompCreateFillets::isActive(void)
{
    return isCreateGeoActive(getActiveGuiDocument());
}

// ======================================================================================

namespace SketcherGui {
    class TrimmingSelection : public Gui::SelectionFilterGate
    {
        App::DocumentObject* object;
    public:
        TrimmingSelection(App::DocumentObject* obj)
            : Gui::SelectionFilterGate(nullPointer()), object(obj)
        {}

        bool allow(App::Document * /*pDoc*/, App::DocumentObject *pObj, const char *sSubName)
        {
            if (pObj != this->object)
                return false;
            if (!sSubName || sSubName[0] == '\0')
                return false;
            std::string element(sSubName);
            if (element.substr(0,4) == "Edge") {
                int GeoId = std::atoi(element.substr(4,4000).c_str()) - 1;
                Sketcher::SketchObject *Sketch = static_cast<Sketcher::SketchObject*>(object);
                const Part::Geometry *geom = Sketch->getGeometry(GeoId);
                if (geom->getTypeId().isDerivedFrom(Part::GeomTrimmedCurve::getClassTypeId())   ||
                    geom->getTypeId() == Part::GeomCircle::getClassTypeId()                     ||
                    geom->getTypeId() == Part::GeomEllipse::getClassTypeId()                    ||
                    geom->getTypeId() == Part::GeomBSplineCurve::getClassTypeId()
                ) {
                    // We do not trim internal geometry of complex geometries
                    if( Sketcher::GeometryFacade::isInternalType(geom, Sketcher::InternalType::None))
                        return true;
                }
            }
            return  false;
        }
    };
}

class DrawSketchHandlerTrimming: public DrawSketchHandler
{
public:
    DrawSketchHandlerTrimming() {}
    virtual ~DrawSketchHandlerTrimming()
    {
        Gui::Selection().rmvSelectionGate();
    }

    virtual void mouseMove(Base::Vector2d onSketchPos) override
    {
        Q_UNUSED(onSketchPos);

        int GeoId = getPreselectCurve();

        if (GeoId > -1) {
            auto sk = static_cast<Sketcher::SketchObject *>(sketchgui->getObject());
            int GeoId1, GeoId2;
            Base::Vector3d intersect1, intersect2;
            if(sk->seekTrimPoints(GeoId, Base::Vector3d(onSketchPos.x,onSketchPos.y,0),
                                  GeoId1, intersect1,
                                  GeoId2, intersect2)) {

                EditMarkers.resize(0);

                if(GeoId1 != Sketcher::GeoEnum::GeoUndef)
                    EditMarkers.emplace_back(intersect1.x, intersect1.y);
                else {
                    auto start = sk->getPoint(GeoId, Sketcher::PointPos::start);
                    EditMarkers.emplace_back(start.x, start.y);
                }

                if(GeoId2 != Sketcher::GeoEnum::GeoUndef)
                    EditMarkers.emplace_back(intersect2.x, intersect2.y);
                else {
                    auto end = sk->getPoint(GeoId, Sketcher::PointPos::end);
                    EditMarkers.emplace_back( end.x, end.y);
                }

                drawEditMarkers(EditMarkers, 2); // maker augmented by two sizes (see supported marker sizes)
            }
        }
        else {
            EditMarkers.resize(0);
            drawEditMarkers(EditMarkers, 2);
        }
    }

    virtual bool pressButton(Base::Vector2d onSketchPos) override
    {
        Q_UNUSED(onSketchPos);
        return true;
    }

    virtual bool releaseButton(Base::Vector2d onSketchPos) override
    {
        int GeoId = getPreselectCurve();
        if (GeoId > -1) {
            const Part::Geometry *geom = sketchgui->getSketchObject()->getGeometry(GeoId);
            if (geom->getTypeId().isDerivedFrom(Part::GeomTrimmedCurve::getClassTypeId())   ||
                geom->getTypeId() == Part::GeomCircle::getClassTypeId()                     ||
                geom->getTypeId() == Part::GeomEllipse::getClassTypeId() ||
                geom->getTypeId() == Part::GeomBSplineCurve::getClassTypeId() ) {
                try {
                    Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Trim edge"));
                    Gui::cmdAppObjectArgs(sketchgui->getObject(), "trim(%d,App.Vector(%f,%f,0))",
                              GeoId, onSketchPos.x, onSketchPos.y);
                    Gui::Command::commitCommand();
                    tryAutoRecompute(static_cast<Sketcher::SketchObject *>(sketchgui->getObject()));
                }
                catch (const Base::Exception& e) {
                    Base::Console().Error("Failed to trim edge: %s\n", e.what());
                    Gui::Command::abortCommand();
                }
            }

            EditMarkers.resize(0);
            drawEditMarkers(EditMarkers);
        }
        else // exit the trimming tool if the user clicked on empty space
            sketchgui->purgeHandler(); // no code after this line, Handler get deleted in ViewProvider

        return true;
    }

private:
    virtual void activated() override
    {
        Gui::Selection().clearSelection();
        Gui::Selection().rmvSelectionGate();
        Gui::Selection().addSelectionGate(new TrimmingSelection(sketchgui->getObject()));
        setCrosshairCursor("Sketcher_Pointer_Trimming");
    }

private:
    std::vector<Base::Vector2d> EditMarkers;
};

DEF_STD_CMD_A(CmdSketcherTrimming)

CmdSketcherTrimming::CmdSketcherTrimming()
  : Command("Sketcher_Trimming")
{
    sAppModule      = "Sketcher";
    sGroup          = "Sketcher";
    sMenuText       = QT_TR_NOOP("Trim edge");
    sToolTipText    = QT_TR_NOOP("Trim an edge with respect to the picked position");
    sWhatsThis      = "Sketcher_Trimming";
    sStatusTip      = sToolTipText;
    sPixmap         = "Sketcher_Trimming";
    sAccel          = "G, T";
    eType           = ForEdit;
}

void CmdSketcherTrimming::activated(int iMsg)
{
    Q_UNUSED(iMsg);
    ActivateHandler(getActiveGuiDocument(), new DrawSketchHandlerTrimming());
}

bool CmdSketcherTrimming::isActive(void)
{
    return isCreateGeoActive(getActiveGuiDocument());
}


// ======================================================================================

namespace SketcherGui {
    class ExtendSelection : public Gui::SelectionFilterGate
    {
        App::DocumentObject* object;
    public:
        ExtendSelection(App::DocumentObject* obj)
            : Gui::SelectionFilterGate(nullPointer())
            , object(obj)
            , disabled(false)
        {}

        bool allow(App::Document * /*pDoc*/, App::DocumentObject *pObj, const char *sSubName)
        {
            if (pObj != this->object)
                return false;
            if (!sSubName || sSubName[0] == '\0')
                return false;
            if (disabled)
                return true;
            std::string element(sSubName);
            if (element.substr(0, 4) == "Edge") {
                int GeoId = std::atoi(element.substr(4, 4000).c_str()) - 1;
                Sketcher::SketchObject *Sketch = static_cast<Sketcher::SketchObject*>(object);
                const Part::Geometry *geom = Sketch->getGeometry(GeoId);
                if (geom->getTypeId() == Part::GeomLineSegment::getClassTypeId() ||
                    geom->getTypeId() == Part::GeomArcOfCircle::getClassTypeId())
                    return true;
            }
            return false;
        }

        void setDisabled(bool isDisabled) {
            disabled = isDisabled;
        }
    protected:
        bool disabled;
    };
}

class DrawSketchHandlerExtend: public DrawSketchHandler
{
public:
    DrawSketchHandlerExtend()
        : Mode(STATUS_SEEK_First)
        , EditCurve(2)
        , BaseGeoId(-1)
        , ExtendFromStart(false)
        , SavedExtendFromStart(false)
        , Increment(0)
    {
    }
    virtual ~DrawSketchHandlerExtend()
    {
        Gui::Selection().rmvSelectionGate();
    }
    enum SelectMode {
        STATUS_SEEK_First,
        STATUS_SEEK_Second,
    };

    virtual void mouseMove(Base::Vector2d onSketchPos) override
    {
        Q_UNUSED(onSketchPos);
        if (Mode == STATUS_SEEK_Second) {
            const Part::Geometry *geom = sketchgui->getSketchObject()->getGeometry(BaseGeoId);
            if (geom->getTypeId() == Part::GeomLineSegment::getClassTypeId()) {
                const Part::GeomLineSegment *lineSeg = static_cast<const Part::GeomLineSegment *>(geom);
                // project point to the existing curve
                Base::Vector3d start3d = lineSeg->getStartPoint();
                Base::Vector3d end3d = lineSeg->getEndPoint();

                Base::Vector2d startPoint = Base::Vector2d(start3d.x, start3d.y);
                Base::Vector2d endPoint = Base::Vector2d(end3d.x, end3d.y);
                Base::Vector2d recenteredLine = endPoint - startPoint;
                Base::Vector2d recenteredPoint = onSketchPos - startPoint;
                Base::Vector2d projection;
                projection.ProjectToLine(recenteredPoint, recenteredLine);
                if (recenteredPoint.Length() < recenteredPoint.Distance(recenteredLine)) {
                    EditCurve[0] = startPoint + projection;
                    EditCurve[1] = endPoint;
                } else {
                    EditCurve[0] = startPoint;
                    EditCurve[1] = startPoint + projection;
                }
                /**
                 * If in-curve, the intuitive behavior is for the line to shrink an amount from
                 * the original click-point.
                 *
                 * If out-of-curve, the intuitive behavior is for the closest line endpoint to
                 * expand.
                 */
                bool inCurve = (projection.Length() < recenteredLine.Length()
                    && projection.GetAngle(recenteredLine) < 0.1); // Two possible values here, M_PI and 0, but 0.1 is to avoid floating point problems.
                if (inCurve) {
                    Increment = SavedExtendFromStart ? -1 * projection.Length() : projection.Length() - recenteredLine.Length();
                    ExtendFromStart = SavedExtendFromStart;
                } else {
                    ExtendFromStart = onSketchPos.Distance(startPoint) < onSketchPos.Distance(endPoint);
                    Increment = ExtendFromStart ? projection.Length() : projection.Length() - recenteredLine.Length();
                }
                drawEdit(EditCurve);

            } else if (geom->getTypeId() == Part::GeomArcOfCircle::getClassTypeId()) {
                const Part::GeomArcOfCircle *arc = static_cast<const Part::GeomArcOfCircle *>(geom);
                Base::Vector3d center = arc->getCenter();
                double radius = arc->getRadius();

                double start, end;
                arc->getRange(start, end, true);
                double arcAngle = end - start;

                Base::Vector2d angle = Base::Vector2d(onSketchPos.x - center.x, onSketchPos.y - center.y);
                Base::Vector2d startAngle = Base::Vector2d(cos(start), sin(start));
                Base::Vector2d endAngle = Base::Vector2d(cos(end), sin(end));

                Base::Vector2d arcHalf = Base::Vector2d(cos(start + arcAngle/ 2.0), sin(start+ arcAngle / 2.0));
                double angleToEndAngle = angle.GetAngle(endAngle);
                double angleToStartAngle = angle.GetAngle(startAngle);


                double modStartAngle = start;
                double modArcAngle = end - start;
                bool outOfArc = arcHalf.GetAngle(angle) * 2.0 > arcAngle;
                if (ExtendFromStart) {
                    bool isCCWFromStart = crossProduct(angle, startAngle) < 0;
                    if (outOfArc) {
                        if (isCCWFromStart) {
                            modStartAngle -= 2*M_PI - angleToStartAngle;
                            modArcAngle += 2*M_PI - angleToStartAngle;
                        } else {
                            modStartAngle -= angleToStartAngle;
                            modArcAngle += angleToStartAngle;
                        }
                    } else {
                        if (isCCWFromStart) {
                            modStartAngle += angleToStartAngle;
                            modArcAngle -= angleToStartAngle;
                        } else {
                            modStartAngle += 2*M_PI - angleToStartAngle;
                            modArcAngle -= 2*M_PI - angleToStartAngle;
                        }
                    }
                } else {
                    bool isCWFromEnd = crossProduct(angle, endAngle) >= 0;
                    if (outOfArc) {
                        if (isCWFromEnd) {
                            modArcAngle += 2*M_PI - angleToEndAngle;
                        } else {
                            modArcAngle += angleToEndAngle;
                        }
                    } else {
                        if (isCWFromEnd) {
                            modArcAngle -= angleToEndAngle;
                        } else {
                            modArcAngle -= 2*M_PI - angleToEndAngle;
                        }
                    }
                }
                Increment = modArcAngle - (end - start);
                for (int i = 0; i < 31; i++) {
                    double angle = modStartAngle + i * modArcAngle/30.0;
                    EditCurve[i] = Base::Vector2d(center.x + radius * cos(angle), center.y + radius * sin(angle));
                }
                drawEdit(EditCurve);
            }
            int curveId = getPreselectCurve();
            if (BaseGeoId != curveId && seekAutoConstraint(SugConstr, onSketchPos, Base::Vector2d(0.f,0.f))) {
                renderSuggestConstraintsCursor(SugConstr);
                return;
            }
        }
    }

    virtual bool pressButton(Base::Vector2d onSketchPos) override
    {
        Q_UNUSED(onSketchPos);
        return true;
    }

    virtual bool releaseButton(Base::Vector2d onSketchPos) override
    {
        Q_UNUSED(onSketchPos);
        if (Mode == STATUS_SEEK_First) {
            BaseGeoId = getPreselectCurve();
            if (BaseGeoId > -1) {
                const Part::Geometry *geom = sketchgui->getSketchObject()->getGeometry(BaseGeoId);
                if (geom->getTypeId() == Part::GeomLineSegment::getClassTypeId()) {
                    const Part::GeomLineSegment *seg = static_cast<const Part::GeomLineSegment *>(geom);
                    Base::Vector3d start3d = seg->getStartPoint();
                    Base::Vector3d end3d = seg->getEndPoint();
                    Base::Vector2d start = Base::Vector2d(start3d.x, start3d.y);
                    Base::Vector2d end = Base::Vector2d(end3d.x, end3d.y);
                    SavedExtendFromStart = (onSketchPos.Distance(start) < onSketchPos.Distance(end));
                    ExtendFromStart = SavedExtendFromStart;
                    Mode = STATUS_SEEK_Second;
                } else if (geom->getTypeId() == Part::GeomArcOfCircle::getClassTypeId()) {
                    const Part::GeomArcOfCircle *arc = static_cast<const Part::GeomArcOfCircle *>(geom);
                    double start, end;
                    arc->getRange(start, end, true);

                    Base::Vector3d center = arc->getCenter();
                    Base::Vector2d angle = Base::Vector2d(onSketchPos.x - center.x, onSketchPos.y - center.y);
                    double angleToStart = angle.GetAngle(Base::Vector2d(cos(start), sin(start)));
                    double angleToEnd = angle.GetAngle(Base::Vector2d(cos(end), sin(end)));
                    ExtendFromStart = (angleToStart < angleToEnd); // move start point if closer to angle than end point
                    EditCurve.resize(31);
                    Mode = STATUS_SEEK_Second;
                }
                filterGate->setDisabled(true);
            }
        } else if (Mode == STATUS_SEEK_Second) {
            try {
                Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Extend edge"));
                Gui::cmdAppObjectArgs(sketchgui->getObject(), "extend(%d, %f, %d)\n", // GeoId, increment, PointPos
                    BaseGeoId, Increment, ExtendFromStart ? static_cast<int>(Sketcher::PointPos::start) : static_cast<int>(Sketcher::PointPos::end));
                Gui::Command::commitCommand();

                ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Mod/Sketcher");
                bool autoRecompute = hGrp->GetBool("AutoRecompute",false);
                if(autoRecompute)
                    Gui::Command::updateActive();

                // constrain chosen point
                if (SugConstr.size() > 0) {
                    createAutoConstraints(SugConstr, BaseGeoId, (ExtendFromStart) ? Sketcher::PointPos::start : Sketcher::PointPos::end);
                    SugConstr.clear();
                }
                bool continuousMode = hGrp->GetBool("ContinuousCreationMode",true);

                if(continuousMode){
                    // This code enables the continuous creation mode.
                    Mode=STATUS_SEEK_First;
                    filterGate->setDisabled(false);
                    EditCurve.clear();
                    drawEdit(EditCurve);
                    EditCurve.resize(2);
                    applyCursor();
                    /* this is ok not to call to purgeHandler
                    * in continuous creation mode because the
                    * handler is destroyed by the quit() method on pressing the
                    * right button of the mouse */
                } else{
                    sketchgui->purgeHandler(); // no code after this line, Handler get deleted in ViewProvider
                }
            }
            catch (const Base::Exception& e) {
                Base::Console().Error("Failed to extend edge: %s\n", e.what());
                Gui::Command::abortCommand();
            }

        } else { // exit extension tool if user clicked on empty space
            BaseGeoId = -1;
            sketchgui->purgeHandler(); // no code after this line, Handler get deleted in ViewProvider
        }
        return true;
    }

private:
    virtual void activated() override
    {
        Q_UNUSED(sketchgui)
        Gui::Selection().clearSelection();
        Gui::Selection().rmvSelectionGate();
        filterGate = new ExtendSelection(sketchgui->getObject());
        Gui::Selection().addSelectionGate(filterGate);
        setCrosshairCursor("Sketcher_Pointer_Extension");
    }

protected:
    SelectMode Mode;
    std::vector<Base::Vector2d> EditCurve;
    int BaseGeoId;
    ExtendSelection* filterGate = nullptr;
    bool ExtendFromStart; // if true, extend from start, else extend from end (circle only)
    bool SavedExtendFromStart;
    double Increment;
    std::vector<AutoConstraint> SugConstr;

private:
    int crossProduct(Base::Vector2d &vec1, Base::Vector2d &vec2) {
        return vec1.x * vec2.y - vec1.y * vec2.x;
    }
};

DEF_STD_CMD_A(CmdSketcherExtend)

//TODO: fix the translations for this
CmdSketcherExtend::CmdSketcherExtend()
  : Command("Sketcher_Extend")
{
    sAppModule      = "Sketcher";
    sGroup          = "Sketcher";
    sMenuText       = QT_TR_NOOP("Extend edge");
    sToolTipText    = QT_TR_NOOP("Extend an edge with respect to the picked position");
    sWhatsThis      = "Sketcher_Extend";
    sStatusTip      = sToolTipText;
    sPixmap         = "Sketcher_Extend";
    sAccel          = "G, Q";
    eType           = ForEdit;
}

void CmdSketcherExtend::activated(int iMsg)
{
    Q_UNUSED(iMsg);
    ActivateHandler(getActiveGuiDocument(), new DrawSketchHandlerExtend());
}

bool CmdSketcherExtend::isActive(void)
{
    return isCreateGeoActive(getActiveGuiDocument());
}


// ======================================================================================

namespace SketcherGui {
    class SplittingSelection : public Gui::SelectionFilterGate
    {
        App::DocumentObject* object;
    public:
        SplittingSelection(App::DocumentObject* obj)
            : Gui::SelectionFilterGate(nullPointer()), object(obj)
        {}

        bool allow(App::Document * /*pDoc*/, App::DocumentObject *pObj, const char *sSubName)
        {
            if (pObj != this->object)
                return false;
            if (!sSubName || sSubName[0] == '\0')
                return false;
            std::string element(sSubName);
            if (element.substr(0,4) == "Edge") {
                int GeoId = std::atoi(element.substr(4,4000).c_str()) - 1;
                Sketcher::SketchObject *Sketch = static_cast<Sketcher::SketchObject*>(object);
                const Part::Geometry *geom = Sketch->getGeometry(GeoId);
                if (geom->getTypeId() == Part::GeomLineSegment::getClassTypeId()
                    || geom->getTypeId() == Part::GeomCircle::getClassTypeId()
                    || geom->getTypeId() == Part::GeomArcOfCircle::getClassTypeId()) {
                    return true;
                }
            }
            return  false;
        }
    };
}

class DrawSketchHandlerSplitting: public DrawSketchHandler
{
public:
    DrawSketchHandlerSplitting() {}
    virtual ~DrawSketchHandlerSplitting()
    {
        Gui::Selection().rmvSelectionGate();
    }

    virtual void mouseMove(Base::Vector2d onSketchPos) override
    {
        Q_UNUSED(onSketchPos);
    }

    virtual bool pressButton(Base::Vector2d onSketchPos) override
    {
        Q_UNUSED(onSketchPos);
        return true;
    }

    virtual bool releaseButton(Base::Vector2d onSketchPos) override
    {
        int GeoId = getPreselectCurve();
        if (GeoId >= 0) {
            const Part::Geometry *geom = sketchgui->getSketchObject()->getGeometry(GeoId);
            if (geom->getTypeId() == Part::GeomLineSegment::getClassTypeId()
                || geom->getTypeId() == Part::GeomCircle::getClassTypeId()
                || geom->getTypeId() == Part::GeomArcOfCircle::getClassTypeId()) {
                try {
                    Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Split edge"));
                    Gui::cmdAppObjectArgs(sketchgui->getObject(), "split(%d,App.Vector(%f,%f,0))",
                              GeoId, onSketchPos.x, onSketchPos.y);
                    Gui::Command::commitCommand();
                    tryAutoRecompute(static_cast<Sketcher::SketchObject *>(sketchgui->getObject()));
                }
                catch (const Base::Exception& e) {
                    Base::Console().Error("Failed to split edge: %s\n", e.what());
                    Gui::Command::abortCommand();
                }
            }
        }
        else {
            sketchgui->purgeHandler();
        }

        return true;
    }

private:
    virtual void activated() override
    {
        Gui::Selection().clearSelection();
        Gui::Selection().rmvSelectionGate();
        Gui::Selection().addSelectionGate(new SplittingSelection(sketchgui->getObject()));
        setCrosshairCursor("Sketcher_Pointer_Splitting");
    }
};

DEF_STD_CMD_A(CmdSketcherSplit)

//TODO: fix the translations for this
CmdSketcherSplit::CmdSketcherSplit()
  : Command("Sketcher_Split")
{
    sAppModule      = "Sketcher";
    sGroup          = "Sketcher";
    sMenuText       = QT_TR_NOOP("Split edge");
    sToolTipText    = QT_TR_NOOP("Splits an edge into two while preserving constraints");
    sWhatsThis      = "Sketcher_Split";
    sStatusTip      = sToolTipText;
    sPixmap         = "Sketcher_Split";
    sAccel          = "G, Z";
    eType           = ForEdit;
}

void CmdSketcherSplit::activated(int iMsg)
{
    Q_UNUSED(iMsg);
    ActivateHandler(getActiveGuiDocument(), new DrawSketchHandlerSplitting());
}

bool CmdSketcherSplit::isActive(void)
{
    return isCreateGeoActive(getActiveGuiDocument());
}


namespace SketcherGui {
    class ExternalSelection : public Gui::SelectionFilterGate
    {
        App::DocumentObject* object;
    public:
        ExternalSelection(App::DocumentObject* obj)
            : Gui::SelectionFilterGate(nullPointer()), object(obj)
        {}

        bool allow(App::Document *pDoc, App::DocumentObject *pObj, const char *sSubName)
        {
            Sketcher::SketchObject *sketch = static_cast<Sketcher::SketchObject*>(object);

            this->notAllowedReason = "";
            Sketcher::SketchObject::eReasonList msg;
            if (!sketch->isExternalAllowed(pDoc, pObj, &msg)){
                switch(msg){
                case Sketcher::SketchObject::rlCircularReference:
                    this->notAllowedReason = QT_TR_NOOP("Linking this will cause circular dependency.");
                    break;
                case Sketcher::SketchObject::rlOtherDoc:
                    this->notAllowedReason = QT_TR_NOOP("This object is in another document.");
                    break;
                case Sketcher::SketchObject::rlOtherBody:
                    this->notAllowedReason = QT_TR_NOOP("This object belongs to another body, can't link.");
                    break;
                case Sketcher::SketchObject::rlOtherPart:
                    this->notAllowedReason = QT_TR_NOOP("This object belongs to another part, can't link.");
                    break;
                default:
                    break;
                }
                return false;
            }

            // Note: its better to search the support of the sketch in case the sketch support is a base plane
            //Part::BodyBase* body = Part::BodyBase::findBodyOf(sketch);
            //if ( body && body->hasFeature ( pObj ) && body->isAfter ( pObj, sketch ) ) {
                // Don't allow selection after the sketch in the same body
                // NOTE: allowness of features in other bodies is handled by SketchObject::isExternalAllowed()
                // TODO may be this should be in SketchObject::isExternalAllowed() (2015-08-07, Fat-Zer)
                //return false;
            //}

            if (!sSubName || sSubName[0] == '\0')
                return false;
            std::string element(sSubName);
            if ((element.size() > 4 && element.substr(0,4) == "Edge") ||
                (element.size() > 6 && element.substr(0,6) == "Vertex") ||
                (element.size() > 4 && element.substr(0,4) == "Face")) {
                return true;
            }
            if (pObj->getTypeId().isDerivedFrom(App::Plane::getClassTypeId())
                /*||
                pObj->getTypeId().isDerivedFrom(Part::Datum::getClassTypeId())*/)
                return true;
            return  false;
        }
    };
}

class DrawSketchHandlerExternal: public DrawSketchHandler
{
public:
    DrawSketchHandlerExternal() {}
    virtual ~DrawSketchHandlerExternal()
    {
        Gui::Selection().rmvSelectionGate();
    }

    virtual void mouseMove(Base::Vector2d onSketchPos) override
    {
        Q_UNUSED(onSketchPos);
        if (Gui::Selection().getPreselection().pObjectName)
            applyCursor();
    }

    virtual bool pressButton(Base::Vector2d onSketchPos) override
    {
        Q_UNUSED(onSketchPos);
        return true;
    }

    virtual bool releaseButton(Base::Vector2d onSketchPos) override
    {
        Q_UNUSED(onSketchPos);
        /* this is ok not to call to purgeHandler
        * in continuous creation mode because the
        * handler is destroyed by the quit() method on pressing the
        * right button of the mouse */
        return true;
    }

    virtual bool onSelectionChanged(const Gui::SelectionChanges& msg) override
    {
        if (msg.Type == Gui::SelectionChanges::AddSelection) {
            App::DocumentObject* obj = sketchgui->getObject()->getDocument()->getObject(msg.pObjectName);
            if (obj == nullptr)
                throw Base::ValueError("Sketcher: External geometry: Invalid object in selection");
            std::string subName(msg.pSubName);
            if (obj->getTypeId().isDerivedFrom(App::Plane::getClassTypeId()) ||
                // obj->getTypeId().isDerivedFrom(Part::Datum::getClassTypeId()) ||
                (subName.size() > 4 && subName.substr(0,4) == "Edge") ||
                (subName.size() > 6 && subName.substr(0,6) == "Vertex") ||
                (subName.size() > 4 && subName.substr(0,4) == "Face")) {
                try {
                    Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Add external geometry"));
                    Gui::cmdAppObjectArgs(sketchgui->getObject(), "addExternal(\"%s\",\"%s\")",
                              msg.pObjectName, msg.pSubName);
                    Gui::Command::commitCommand();

                    // adding external geometry does not require a solve() per se (the DoF is the same),
                    // however a solve is required to update the amount of solver geometry, because we only
                    // redraw a changed Sketch if the solver geometry amount is the same as the SkethObject
                    // geometry amount (as this avoids other issues).
                    // This solver is a very low cost one anyway (there is actually nothing to solve).
                    tryAutoRecomputeIfNotSolve(static_cast<Sketcher::SketchObject *>(sketchgui->getObject()));

                    Gui::Selection().clearSelection();
                /* this is ok not to call to purgeHandler
                * in continuous creation mode because the
                * handler is destroyed by the quit() method on pressing the
                * right button of the mouse */
                }
                catch (const Base::Exception& e) {
                    Base::Console().Error("Failed to add external geometry: %s\n", e.what());
                    Gui::Selection().clearSelection();
                    Gui::Command::abortCommand();
                }
                return true;
            }
        }
        return false;
    }

private:
    virtual void activated() override
    {
        setAxisPickStyle(false);
        Gui::MDIView *mdi = Gui::Application::Instance->activeDocument()->getActiveView();
        Gui::View3DInventorViewer *viewer;
        viewer = static_cast<Gui::View3DInventor *>(mdi)->getViewer();

        SoNode* root = viewer->getSceneGraph();
        static_cast<Gui::SoFCUnifiedSelection*>(root)->selectionRole.setValue(true);

        Gui::Selection().clearSelection();
        Gui::Selection().rmvSelectionGate();
        Gui::Selection().addSelectionGate(new ExternalSelection(sketchgui->getObject()));
        setCrosshairCursor("Sketcher_Pointer_External");
    }

    virtual void deactivated() override
    {
        Q_UNUSED(sketchgui);
        setAxisPickStyle(true);
    }
};

DEF_STD_CMD_A(CmdSketcherExternal)

CmdSketcherExternal::CmdSketcherExternal()
  : Command("Sketcher_External")
{
    sAppModule      = "Sketcher";
    sGroup          = "Sketcher";
    sMenuText       = QT_TR_NOOP("External geometry");
    sToolTipText    = QT_TR_NOOP("Create an edge linked to an external geometry");
    sWhatsThis      = "Sketcher_External";
    sStatusTip      = sToolTipText;
    sPixmap         = "Sketcher_External";
    sAccel          = "G, X";
    eType           = ForEdit;
}

void CmdSketcherExternal::activated(int iMsg)
{
    Q_UNUSED(iMsg);
    ActivateHandler(getActiveGuiDocument(), new DrawSketchHandlerExternal());
}

bool CmdSketcherExternal::isActive(void)
{
    return isCreateGeoActive(getActiveGuiDocument());
}

// ======================================================================================

namespace SketcherGui {
    class CarbonCopySelection : public Gui::SelectionFilterGate
    {
        App::DocumentObject* object;
    public:
        CarbonCopySelection(App::DocumentObject* obj)
        : Gui::SelectionFilterGate(nullPointer()), object(obj)
        {}

        bool allow(App::Document *pDoc, App::DocumentObject *pObj, const char *sSubName)
        {
            Q_UNUSED(sSubName);

            Sketcher::SketchObject *sketch = static_cast<Sketcher::SketchObject*>(object);
            sketch->setAllowOtherBody(QApplication::keyboardModifiers() == Qt::ControlModifier || QApplication::keyboardModifiers() == (Qt::ControlModifier | Qt::AltModifier));
            sketch->setAllowUnaligned(QApplication::keyboardModifiers() == (Qt::ControlModifier | Qt::AltModifier));

            this->notAllowedReason = "";
            Sketcher::SketchObject::eReasonList msg;
            // Reusing code: All good reasons not to allow a carbon copy
            bool xinv = false, yinv = false;
            if (!sketch->isCarbonCopyAllowed(pDoc, pObj, xinv, yinv, &msg)){
                switch(msg){
                    case Sketcher::SketchObject::rlCircularReference:
                        this->notAllowedReason = QT_TR_NOOP("Carbon copy would cause a circular dependency.");
                        break;
                    case Sketcher::SketchObject::rlOtherDoc:
                        this->notAllowedReason = QT_TR_NOOP("This object is in another document.");
                        break;
                    case Sketcher::SketchObject::rlOtherBody:
                        this->notAllowedReason = QT_TR_NOOP("This object belongs to another body. Hold Ctrl to allow cross-references.");
                        break;
                    case Sketcher::SketchObject::rlOtherBodyWithLinks:
                        this->notAllowedReason = QT_TR_NOOP("This object belongs to another body and it contains external geometry. Cross-reference not allowed.");
                        break;
                    case Sketcher::SketchObject::rlOtherPart:
                        this->notAllowedReason = QT_TR_NOOP("This object belongs to another part.");
                        break;
                    case Sketcher::SketchObject::rlNonParallel:
                        this->notAllowedReason = QT_TR_NOOP("The selected sketch is not parallel to this sketch. Hold Ctrl+Alt to allow non-parallel sketches.");
                        break;
                    case Sketcher::SketchObject::rlAxesMisaligned:
                        this->notAllowedReason = QT_TR_NOOP("The XY axes of the selected sketch do not have the same direction as this sketch. Hold Ctrl+Alt to disregard it.");
                        break;
                    case Sketcher::SketchObject::rlOriginsMisaligned:
                        this->notAllowedReason = QT_TR_NOOP("The origin of the selected sketch is not aligned with the origin of this sketch. Hold Ctrl+Alt to disregard it.");
                        break;
                    default:
                        break;
                }
                return false;
            }
            // Carbon copy only works on sketches that are not disallowed (e.g. would produce a circular reference)
            return  true;
        }
    };
}


class DrawSketchHandlerCarbonCopy: public DrawSketchHandler
{
public:
    DrawSketchHandlerCarbonCopy() {}
    virtual ~DrawSketchHandlerCarbonCopy()
    {
        Gui::Selection().rmvSelectionGate();
    }

    virtual void mouseMove(Base::Vector2d onSketchPos) override
    {
        Q_UNUSED(onSketchPos);
        if (Gui::Selection().getPreselection().pObjectName)
            applyCursor();
    }

    virtual bool pressButton(Base::Vector2d onSketchPos) override
    {
        Q_UNUSED(onSketchPos);
        return true;
    }

    virtual bool releaseButton(Base::Vector2d onSketchPos) override
    {
        Q_UNUSED(onSketchPos);
        /* this is ok not to call to purgeHandler
            * in continuous creation mode because the
            * handler is destroyed by the quit() method on pressing the
            * right button of the mouse */
        return true;
    }

    virtual bool onSelectionChanged(const Gui::SelectionChanges& msg) override
    {
        if (msg.Type == Gui::SelectionChanges::AddSelection) {
            App::DocumentObject* obj = sketchgui->getObject()->getDocument()->getObject(msg.pObjectName);
            if (obj == nullptr)
                throw Base::ValueError("Sketcher: Carbon Copy: Invalid object in selection");

            if (obj->getTypeId() == Sketcher::SketchObject::getClassTypeId()) {

                try {
                    Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Add carbon copy"));
                    Gui::cmdAppObjectArgs(sketchgui->getObject(), "carbonCopy(\"%s\",%s)",
                                            msg.pObjectName, geometryCreationMode==Construction?"True":"False");

                    Gui::Command::commitCommand();

                    tryAutoRecomputeIfNotSolve(static_cast<Sketcher::SketchObject *>(sketchgui->getObject()));

                    Gui::Selection().clearSelection();
                    /* this is ok not to call to purgeHandler
                        * in continuous creation mode because the
                        * handler is destroyed by the quit() method on pressing the
                        * right button of the mouse */
                }
                catch (const Base::Exception& e) {
                    Base::Console().Error("Failed to add carbon copy: %s\n", e.what());
                    Gui::Command::abortCommand();
                }
                return true;
                }
        }
        return false;
    }

private:
    virtual void activated() override
    {
        setAxisPickStyle(false);
        Gui::MDIView *mdi = Gui::Application::Instance->activeDocument()->getActiveView();
        Gui::View3DInventorViewer *viewer;
        viewer = static_cast<Gui::View3DInventor *>(mdi)->getViewer();

        SoNode* root = viewer->getSceneGraph();
        static_cast<Gui::SoFCUnifiedSelection*>(root)->selectionRole.setValue(true);

        Gui::Selection().clearSelection();
        Gui::Selection().rmvSelectionGate();
        Gui::Selection().addSelectionGate(new CarbonCopySelection(sketchgui->getObject()));
        setCrosshairCursor("Sketcher_Pointer_CarbonCopy");
    }

    virtual void deactivated() override
    {
        Q_UNUSED(sketchgui);
        setAxisPickStyle(true);
    }
};

DEF_STD_CMD_AU(CmdSketcherCarbonCopy)

CmdSketcherCarbonCopy::CmdSketcherCarbonCopy()
: Command("Sketcher_CarbonCopy")
{
    sAppModule      = "Sketcher";
    sGroup          = "Sketcher";
    sMenuText       = QT_TR_NOOP("Carbon copy");
    sToolTipText    = QT_TR_NOOP("Copies the geometry of another sketch");
    sWhatsThis      = "Sketcher_CarbonCopy";
    sStatusTip      = sToolTipText;
    sPixmap         = "Sketcher_CarbonCopy";
    sAccel          = "G, W";
    eType           = ForEdit;
}

CONSTRUCTION_UPDATE_ACTION(CmdSketcherCarbonCopy, "Sketcher_CarbonCopy")

void CmdSketcherCarbonCopy::activated(int iMsg)
{
    Q_UNUSED(iMsg);
    ActivateHandler(getActiveGuiDocument(), new DrawSketchHandlerCarbonCopy());
}

bool CmdSketcherCarbonCopy::isActive(void)
{
    return isCreateGeoActive(getActiveGuiDocument());
}


/**
 * Create Slot
 */
class DrawSketchHandlerSlot : public DrawSketchHandler
{
public:
    DrawSketchHandlerSlot()
        : Mode(STATUS_SEEK_First)
        , SnapMode(SNAP_MODE_Free)
        , SnapDir(SNAP_DIR_Horz)
        , dx(0), dy(0), r(0)
        , EditCurve(35)
    {
    }
    virtual ~DrawSketchHandlerSlot() {}
    /// mode table
    enum BoxMode {
        STATUS_SEEK_First,      /**< enum value ----. */
        STATUS_SEEK_Second,     /**< enum value ----. */
        STATUS_End
    };

    enum SNAP_MODE
    {
        SNAP_MODE_Free,
        SNAP_MODE_Straight
    };

    enum SNAP_DIR
    {
        SNAP_DIR_Horz,
        SNAP_DIR_Vert
    };

    virtual void mouseMove(Base::Vector2d onSketchPos) override
    {

        if (Mode == STATUS_SEEK_First) {
            setPositionText(onSketchPos);
            if (seekAutoConstraint(sugConstr1, onSketchPos, Base::Vector2d(0.f, 0.f))) {
                renderSuggestConstraintsCursor(sugConstr1);
                return;
            }
        }
        else if (Mode == STATUS_SEEK_Second) {
            dx = onSketchPos.x - StartPos.x;
            dy = onSketchPos.y - StartPos.y;

            if(QApplication::keyboardModifiers() == Qt::ControlModifier)
                SnapMode = SNAP_MODE_Straight;
            else
                SnapMode = SNAP_MODE_Free;

            double a = 0;
            double rev = 0;
            if (fabs(dx) > fabs(dy)) {
                r = fabs(dx) / 4;
                rev = Base::sgn(dx);
                SnapDir = SNAP_DIR_Horz;
                if (SnapMode == SNAP_MODE_Straight) dy = 0;
            }
            else {
                r = fabs(dy) / 4;
                a = 8;
                rev = Base::sgn(dy);
                SnapDir = SNAP_DIR_Vert;
                if (SnapMode == SNAP_MODE_Straight) dx = 0;
            }

            // draw the arcs with each 16 segments
            for (int i = 0; i < 17; i++) {
                // first get the position at the arc
                // if a is 0, the end points of the arc are at the y-axis, if it is 8, they are on the x-axis
                double angle = (i + a) * M_PI / 16.0;
                double rx = -r * rev * sin(angle);
                double ry = r * rev * cos(angle);
                // now apply the rotation matrix according to the angle between StartPos and onSketchPos
                if (!(dx == 0 || dy == 0)) {
                    double rotAngle = atan(dy / dx);
                    if (a > 0)
                        rotAngle = -atan(dx / dy);
                    double rxRot = rx * cos(rotAngle) - ry * sin(rotAngle);
                    double ryRot = rx * sin(rotAngle) + ry * cos(rotAngle);
                    rx = rxRot;
                    ry = ryRot;
                }
                EditCurve[i] = Base::Vector2d(StartPos.x + rx, StartPos.y + ry);
                EditCurve[17 + i] = Base::Vector2d(StartPos.x + dx - rx, StartPos.y + dy - ry);
            }
            EditCurve[34] = EditCurve[0];

            SbString text;
            text.sprintf(" (%.1fR %.1fL)", r, sqrt(dx * dx + dy * dy));
            setPositionText(onSketchPos, text);

            drawEdit(EditCurve);
            if (seekAutoConstraint(sugConstr2, onSketchPos, Base::Vector2d(dx, dy), AutoConstraint::VERTEX_NO_TANGENCY)) {
                renderSuggestConstraintsCursor(sugConstr2);
                return;
            }
        }
        applyCursor();
    }

    virtual bool pressButton(Base::Vector2d onSketchPos) override
    {
        if (Mode == STATUS_SEEK_First) {
            StartPos = onSketchPos;
            Mode = STATUS_SEEK_Second;
        }
        else {
            Mode = STATUS_End;
        }
        return true;
    }

    virtual bool releaseButton(Base::Vector2d onSketchPos) override
    {
        Q_UNUSED(onSketchPos);
        if (Mode == STATUS_End) {
            unsetCursor();
            resetPositionText();

            int firstCurve = getHighestCurveIndex() + 1;
            // add the geometry to the sketch
            // first determine the rotation angle for the first arc
            double start, end;
            if (fabs(dx) > fabs(dy)) {
                if (dx > 0) {
                    start = 0.5 * M_PI;
                    end = 1.5 * M_PI;
                }
                else {
                    start = 1.5 * M_PI;
                    end = 0.5 * M_PI;
                }
            }
            else {
                if (dy > 0) {
                    start = -M_PI;
                    end = 0;
                }
                else {
                    start = 0;
                    end = -M_PI;
                }
            }

            try {
                Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Add slot"));

                AutoConstraint lastCons = {Sketcher::None, Sketcher::GeoEnum::GeoUndef, Sketcher::PointPos::none};

                if (!sugConstr2.empty()) lastCons = sugConstr2.back();

                ostringstream snapCon = ostringstream("");
                if (SnapMode == SNAP_MODE_Straight) {
                    snapCon << "conList.append(Sketcher.Constraint('";
                    if (SnapDir == SNAP_DIR_Horz) {
                        snapCon << "Horizontal";
                    }
                    else {
                        snapCon << "Vertical";
                    }
                    snapCon << "'," << firstCurve + 2 << "))\n";

                    // If horizontal/vertical already applied because of snap, do not duplicate with Autocontraint
                    if (lastCons.Type == Sketcher::Horizontal || lastCons.Type == Sketcher::Vertical)
                        sugConstr2.pop_back();
                }
                else {
                    // If horizontal/vertical Autoconstraint suggested, applied it on first line (rather than last arc)
                    if (lastCons.Type == Sketcher::Horizontal || lastCons.Type == Sketcher::Vertical)
                        sugConstr2.back().GeoId = firstCurve + 2;
                }

                Gui::Command::doCommand(Gui::Command::Doc,
                    "geoList = []\n"
                    "geoList.append(Part.ArcOfCircle(Part.Circle(App.Vector(%f, %f, 0), App.Vector(0, 0, 1), %f), %f, %f))\n"
                    "geoList.append(Part.ArcOfCircle(Part.Circle(App.Vector(%f, %f ,0), App.Vector(0, 0, 1), %f), %f, %f))\n"
                    "geoList.append(Part.LineSegment(App.Vector(%f, %f, 0), App.Vector(%f, %f, 0)))\n"
                    "geoList.append(Part.LineSegment(App.Vector(%f, %f, 0), App.Vector(%f, %f, 0)))\n"
                    "%s.addGeometry(geoList, %s)\n"
                    "conList = []\n"
                    "conList.append(Sketcher.Constraint('Tangent', %i, 2, %i, 1))\n"
                    "conList.append(Sketcher.Constraint('Tangent', %i, 2, %i, 1))\n"
                    "conList.append(Sketcher.Constraint('Tangent', %i, 2, %i, 1))\n"
                    "conList.append(Sketcher.Constraint('Tangent', %i, 2, %i, 1))\n"
                    "conList.append(Sketcher.Constraint('Equal', %i, %i))\n"
                    "%s"
                    "%s.addConstraint(conList)\n"
                    "del geoList, conList\n",
                    StartPos.x, StartPos.y,           // center of the arc1
                    r,                                // radius arc1
                    start, end,                       // start and end angle of arc1
                    StartPos.x + dx, StartPos.y + dy, // center of the arc2
                    r,                                // radius arc2
                    end, end + M_PI,                  // start and end angle of arc2
                    EditCurve[16].x, EditCurve[16].y, EditCurve[17].x, EditCurve[17].y, // line1
                    EditCurve[33].x, EditCurve[33].y, EditCurve[34].x, EditCurve[34].y, // line2
                    Gui::Command::getObjectCmd(sketchgui->getObject()).c_str(), // the sketch
                    geometryCreationMode == Construction ? "True" : "False", // geometry as construction or not
                    firstCurve, firstCurve + 2,     // tangent1
                    firstCurve + 2, firstCurve + 1, // tangent2
                    firstCurve + 1, firstCurve + 3, // tangent3
                    firstCurve + 3, firstCurve,     // tangent4
                    firstCurve, firstCurve + 1,     // equal constraint
                    snapCon.str().c_str(),          // horizontal/vertical constraint if snapping
                    Gui::Command::getObjectCmd(sketchgui->getObject()).c_str()); // the sketch

                Gui::Command::commitCommand();

                // add auto constraints at the center of the first arc
                if (sugConstr1.size() > 0) {
                    createAutoConstraints(sugConstr1, getHighestCurveIndex() - 3, Sketcher::PointPos::mid);
                    sugConstr1.clear();
                }

                // add auto constraints at the center of the second arc
                if (sugConstr2.size() > 0) {
                    createAutoConstraints(sugConstr2, getHighestCurveIndex() - 2, Sketcher::PointPos::mid);
                    sugConstr2.clear();
                }

                tryAutoRecomputeIfNotSolve(static_cast<Sketcher::SketchObject*>(sketchgui->getObject()));
            }
            catch (const Base::Exception& e) {
                Base::Console().Error("Failed to add slot: %s\n", e.what());
                Gui::Command::abortCommand();

                tryAutoRecompute(static_cast<Sketcher::SketchObject*>(sketchgui->getObject()));
            }
            ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Mod/Sketcher");
            bool continuousMode = hGrp->GetBool("ContinuousCreationMode", true);

            if (continuousMode) {
                // This code enables the continuous creation mode.
                Mode = STATUS_SEEK_First;
                EditCurve.clear();
                drawEdit(EditCurve);
                EditCurve.resize(35);
                applyCursor();
                /* this is ok not to call to purgeHandler
                * in continuous creation mode because the
                * handler is destroyed by the quit() method on pressing the
                * right button of the mouse */
            }
            else {
                sketchgui->purgeHandler(); // no code after this line, Handler get deleted in ViewProvider
            }
            SnapMode = SNAP_MODE_Straight;
        }
        return true;
    }
private:
    virtual void activated() override
    {
        setCrosshairCursor("Sketcher_Pointer_Slot");
    }

protected:
    BoxMode Mode;
    SNAP_MODE SnapMode;
    SNAP_DIR SnapDir;
    Base::Vector2d StartPos;
    double dx, dy, r;
    std::vector<Base::Vector2d> EditCurve;
    std::vector<AutoConstraint> sugConstr1, sugConstr2;
};

DEF_STD_CMD_AU(CmdSketcherCreateSlot)

CmdSketcherCreateSlot::CmdSketcherCreateSlot()
    : Command("Sketcher_CreateSlot")
{
    sAppModule = "Sketcher";
    sGroup = "Sketcher";
    sMenuText = QT_TR_NOOP("Create slot");
    sToolTipText = QT_TR_NOOP("Create a slot in the sketch");
    sWhatsThis = "Sketcher_CreateSlot";
    sStatusTip = sToolTipText;
    sPixmap = "Sketcher_CreateSlot";
    sAccel  = "G, S";
    eType = ForEdit;
}

CONSTRUCTION_UPDATE_ACTION(CmdSketcherCreateSlot, "Sketcher_CreateSlot")

void CmdSketcherCreateSlot::activated(int iMsg)
{
    Q_UNUSED(iMsg);
    ActivateHandler(getActiveGuiDocument(), new DrawSketchHandlerSlot());
}

bool CmdSketcherCreateSlot::isActive(void)
{
    return isCreateGeoActive(getActiveGuiDocument());
}

/* Create Regular Polygon ==============================================*/

class DrawSketchHandlerRegularPolygon: public DrawSketchHandler
{
public:
    DrawSketchHandlerRegularPolygon( size_t nof_corners ):
        Corners( nof_corners ),
        AngleOfSeparation( 2.0*M_PI/static_cast<double>(Corners) ),
        cos_v( cos( AngleOfSeparation ) ),
        sin_v( sin( AngleOfSeparation ) ),
        Mode(STATUS_SEEK_First),
        EditCurve(Corners+1)
    {
    }
    virtual ~DrawSketchHandlerRegularPolygon(){}
    /// mode table
    enum SelectMode {
        STATUS_SEEK_First,      /**< enum value ----. */
        STATUS_SEEK_Second,     /**< enum value ----. */
        STATUS_End
    };

    virtual void mouseMove(Base::Vector2d onSketchPos) override
    {

        if (Mode==STATUS_SEEK_First) {
            setPositionText(onSketchPos);
            if (seekAutoConstraint(sugConstr1, onSketchPos, Base::Vector2d(0.f,0.f))) {
                renderSuggestConstraintsCursor(sugConstr1);
                return;
            }
        }
        else if (Mode==STATUS_SEEK_Second) {
            EditCurve[0]= Base::Vector2d(onSketchPos.x, onSketchPos.y);
            EditCurve[Corners]= Base::Vector2d(onSketchPos.x, onSketchPos.y);

            Base::Vector2d dV = onSketchPos - StartPos;
            double rx = dV.x;
            double ry = dV.y;
            for (int i=1; i < static_cast<int>(Corners); i++) {
                const double old_rx = rx;
                rx = cos_v * rx - sin_v * ry;
                ry = cos_v * ry + sin_v * old_rx;
                EditCurve[i] = Base::Vector2d(StartPos.x + rx, StartPos.y + ry);
            }

            // Display radius for user
            const float radius = dV.Length();
            const float angle = ( 180.0 / M_PI ) * atan2( dV.y, dV.x );

            SbString text;
            text.sprintf(" (%.1fR %.1fdeg)", radius, angle );
            setPositionText(onSketchPos, text);

            drawEdit(EditCurve);
            if (seekAutoConstraint(sugConstr2, onSketchPos, Base::Vector2d(0.f,0.f))) {
                renderSuggestConstraintsCursor(sugConstr2);
                return;
            }
        }
        applyCursor();
    }

    virtual bool pressButton(Base::Vector2d onSketchPos) override
    {
        if (Mode==STATUS_SEEK_First){
            StartPos = onSketchPos;
            Mode = STATUS_SEEK_Second;
        }
        else {
            Mode = STATUS_End;
        }
        return true;
    }

    virtual bool releaseButton(Base::Vector2d onSketchPos) override
    {
        Q_UNUSED(onSketchPos);
        if (Mode==STATUS_End){
            unsetCursor();
            resetPositionText();
            Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Add hexagon"));

            try {
                Gui::Command::doCommand(Gui::Command::Doc,
                        "import ProfileLib.RegularPolygon\n"
                        "ProfileLib.RegularPolygon.makeRegularPolygon(%s,%i,App.Vector(%f,%f,0),App.Vector(%f,%f,0),%s)",
                                            Gui::Command::getObjectCmd(sketchgui->getObject()).c_str(),
                                            Corners,
                                            StartPos.x,StartPos.y,EditCurve[0].x,EditCurve[0].y,
                                            geometryCreationMode==Construction?"True":"False");

                Gui::Command::commitCommand();

                // add auto constraints at the center of the polygon
                if (sugConstr1.size() > 0) {
                    createAutoConstraints(sugConstr1, getHighestCurveIndex(), Sketcher::PointPos::mid);
                    sugConstr1.clear();
                }

                // add auto constraints to the last side of the polygon
                if (sugConstr2.size() > 0) {
                    createAutoConstraints(sugConstr2, getHighestCurveIndex() - 1, Sketcher::PointPos::end);
                    sugConstr2.clear();
                }

                tryAutoRecomputeIfNotSolve(static_cast<Sketcher::SketchObject *>(sketchgui->getObject()));
            }
            catch (const Base::Exception& e) {
                Base::Console().Error("Failed to add hexagon: %s\n", e.what());
                Gui::Command::abortCommand();

                tryAutoRecompute(static_cast<Sketcher::SketchObject *>(sketchgui->getObject()));
            }

            ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Mod/Sketcher");
            bool continuousMode = hGrp->GetBool("ContinuousCreationMode",true);

            if(continuousMode){
                // This code enables the continuous creation mode.
                Mode=STATUS_SEEK_First;
                EditCurve.clear();
                drawEdit(EditCurve);
                EditCurve.resize(Corners+1);
                applyCursor();
                /* this is ok not to call to purgeHandler
                * in continuous creation mode because the
                * handler is destroyed by the quit() method on pressing the
                * right button of the mouse */
            }
            else{
                sketchgui->purgeHandler(); // no code after this line, Handler get deleted in ViewProvider
            }
        }
        return true;
    }

private:
    virtual void activated() override
    {
        setCrosshairCursor("Sketcher_Pointer_Regular_Polygon");
    }
protected:
    const size_t Corners;
    const double AngleOfSeparation;
    const double cos_v, sin_v;
    SelectMode Mode;
    Base::Vector2d StartPos;
    std::vector<Base::Vector2d> EditCurve;
    std::vector<AutoConstraint> sugConstr1, sugConstr2;
};


DEF_STD_CMD_AU(CmdSketcherCreateTriangle)

CmdSketcherCreateTriangle::CmdSketcherCreateTriangle()
  : Command("Sketcher_CreateTriangle")
{
    sAppModule      = "Sketcher";
    sGroup          = "Sketcher";
    sMenuText       = QT_TR_NOOP("Create equilateral triangle");
    sToolTipText    = QT_TR_NOOP("Create an equilateral triangle in the sketch");
    sWhatsThis      = "Sketcher_CreateTriangle";
    sStatusTip      = sToolTipText;
    sPixmap         = "Sketcher_CreateTriangle";
    sAccel          = "G, P, 3";
    eType           = ForEdit;
}

CONSTRUCTION_UPDATE_ACTION(CmdSketcherCreateTriangle, "Sketcher_CreateTriangle")

void CmdSketcherCreateTriangle::activated(int iMsg)
{
    Q_UNUSED(iMsg);
    ActivateHandler(getActiveGuiDocument(),new DrawSketchHandlerRegularPolygon(3) );
}

bool CmdSketcherCreateTriangle::isActive(void)
{
    return isCreateGeoActive(getActiveGuiDocument());
}

DEF_STD_CMD_AU(CmdSketcherCreateSquare)

CmdSketcherCreateSquare::CmdSketcherCreateSquare()
  : Command("Sketcher_CreateSquare")
{
    sAppModule      = "Sketcher";
    sGroup          = "Sketcher";
    sMenuText       = QT_TR_NOOP("Create square");
    sToolTipText    = QT_TR_NOOP("Create a square in the sketch");
    sWhatsThis      = "Sketcher_CreateSquare";
    sStatusTip      = sToolTipText;
    sPixmap         = "Sketcher_CreateSquare";
    sAccel          = "G, P, 4";
    eType           = ForEdit;
}

CONSTRUCTION_UPDATE_ACTION(CmdSketcherCreateSquare, "Sketcher_CreateSquare")

void CmdSketcherCreateSquare::activated(int iMsg)
{
    Q_UNUSED(iMsg);
    ActivateHandler(getActiveGuiDocument(),new DrawSketchHandlerRegularPolygon(4) );
}

bool CmdSketcherCreateSquare::isActive(void)
{
    return isCreateGeoActive(getActiveGuiDocument());
}

DEF_STD_CMD_AU(CmdSketcherCreatePentagon)

CmdSketcherCreatePentagon::CmdSketcherCreatePentagon()
  : Command("Sketcher_CreatePentagon")
{
    sAppModule      = "Sketcher";
    sGroup          = "Sketcher";
    sMenuText       = QT_TR_NOOP("Create pentagon");
    sToolTipText    = QT_TR_NOOP("Create a pentagon in the sketch");
    sWhatsThis      = "Sketcher_CreatePentagon";
    sStatusTip      = sToolTipText;
    sPixmap         = "Sketcher_CreatePentagon";
    sAccel          = "G, P, 5";
    eType           = ForEdit;
}

CONSTRUCTION_UPDATE_ACTION(CmdSketcherCreatePentagon, "Sketcher_CreatePentagon")

void CmdSketcherCreatePentagon::activated(int iMsg)
{
    Q_UNUSED(iMsg);
    ActivateHandler(getActiveGuiDocument(),new DrawSketchHandlerRegularPolygon(5) );
}

bool CmdSketcherCreatePentagon::isActive(void)
{
    return isCreateGeoActive(getActiveGuiDocument());
}


DEF_STD_CMD_AU(CmdSketcherCreateHexagon)

CmdSketcherCreateHexagon::CmdSketcherCreateHexagon()
  : Command("Sketcher_CreateHexagon")
{
    sAppModule      = "Sketcher";
    sGroup          = "Sketcher";
    sMenuText       = QT_TR_NOOP("Create hexagon");
    sToolTipText    = QT_TR_NOOP("Create a hexagon in the sketch");
    sWhatsThis      = "Sketcher_CreateHexagon";
    sStatusTip      = sToolTipText;
    sPixmap         = "Sketcher_CreateHexagon";
    sAccel          = "G, P, 6";
    eType           = ForEdit;
}

CONSTRUCTION_UPDATE_ACTION(CmdSketcherCreateHexagon, "Sketcher_CreateHexagon")

void CmdSketcherCreateHexagon::activated(int iMsg)
{
    Q_UNUSED(iMsg);
    ActivateHandler(getActiveGuiDocument(),new DrawSketchHandlerRegularPolygon(6) );
}

bool CmdSketcherCreateHexagon::isActive(void)
{
    return isCreateGeoActive(getActiveGuiDocument());
}

DEF_STD_CMD_AU(CmdSketcherCreateHeptagon)

CmdSketcherCreateHeptagon::CmdSketcherCreateHeptagon()
  : Command("Sketcher_CreateHeptagon")
{
    sAppModule      = "Sketcher";
    sGroup          = "Sketcher";
    sMenuText       = QT_TR_NOOP("Create heptagon");
    sToolTipText    = QT_TR_NOOP("Create a heptagon in the sketch");
    sWhatsThis      = "Sketcher_CreateHeptagon";
    sStatusTip      = sToolTipText;
    sPixmap         = "Sketcher_CreateHeptagon";
    sAccel          = "G, P, 7";
    eType           = ForEdit;
}

CONSTRUCTION_UPDATE_ACTION(CmdSketcherCreateHeptagon, "Sketcher_CreateHeptagon")

void CmdSketcherCreateHeptagon::activated(int iMsg)
{
    Q_UNUSED(iMsg);
    ActivateHandler(getActiveGuiDocument(),new DrawSketchHandlerRegularPolygon(7) );
}

bool CmdSketcherCreateHeptagon::isActive(void)
{
    return isCreateGeoActive(getActiveGuiDocument());
}

DEF_STD_CMD_AU(CmdSketcherCreateOctagon)

CmdSketcherCreateOctagon::CmdSketcherCreateOctagon()
  : Command("Sketcher_CreateOctagon")
{
    sAppModule      = "Sketcher";
    sGroup          = "Sketcher";
    sMenuText       = QT_TR_NOOP("Create octagon");
    sToolTipText    = QT_TR_NOOP("Create an octagon in the sketch");
    sWhatsThis      = "Sketcher_CreateOctagon";
    sStatusTip      = sToolTipText;
    sPixmap         = "Sketcher_CreateOctagon";
    sAccel          = "G, P, 8";
    eType           = ForEdit;
}

CONSTRUCTION_UPDATE_ACTION(CmdSketcherCreateOctagon, "Sketcher_CreateOctagon")

void CmdSketcherCreateOctagon::activated(int iMsg)
{
    Q_UNUSED(iMsg);
    ActivateHandler(getActiveGuiDocument(),new DrawSketchHandlerRegularPolygon(8) );
}

bool CmdSketcherCreateOctagon::isActive(void)
{
    return isCreateGeoActive(getActiveGuiDocument());
}

DEF_STD_CMD_AU(CmdSketcherCreateRegularPolygon)

CmdSketcherCreateRegularPolygon::CmdSketcherCreateRegularPolygon()
: Command("Sketcher_CreateRegularPolygon")
{
    sAppModule      = "Sketcher";
    sGroup          = "Sketcher";
    sMenuText       = QT_TR_NOOP("Create regular polygon");
    sToolTipText    = QT_TR_NOOP("Create a regular polygon in the sketch");
    sWhatsThis      = "Sketcher_CreateRegularPolygon";
    sStatusTip      = sToolTipText;
    sPixmap         = "Sketcher_CreateRegularPolygon";
    sAccel          = "G, P, R";
    eType           = ForEdit;
}

CONSTRUCTION_UPDATE_ACTION(CmdSketcherCreateRegularPolygon, "Sketcher_CreateRegularPolygon")

void CmdSketcherCreateRegularPolygon::activated(int iMsg)
{
    Q_UNUSED(iMsg);

    // Pop-up asking for values
    /*SketcherRegularPolygonDialog srpd;
    if (srpd.exec() == QDialog::Accepted)
        ActivateHandler(getActiveGuiDocument(),new DrawSketchHandlerRegularPolygon(srpd.sides));*/
    ActivateHandler(getActiveGuiDocument(),new DrawSketchHandlerRegularPolygon(1));

}

bool CmdSketcherCreateRegularPolygon::isActive(void)
{
    return isCreateGeoActive(getActiveGuiDocument());
}

DEF_STD_CMD_ACLU(CmdSketcherCompCreateRegularPolygon)

CmdSketcherCompCreateRegularPolygon::CmdSketcherCompCreateRegularPolygon()
  : Command("Sketcher_CompCreateRegularPolygon")
{
    sAppModule      = "Sketcher";
    sGroup          = "Sketcher";
    sMenuText       = QT_TR_NOOP("Create regular polygon");
    sToolTipText    = QT_TR_NOOP("Create a regular polygon in the sketcher");
    sWhatsThis      = "Sketcher_CompCreateRegularPolygon";
    sStatusTip      = sToolTipText;
    sAccel          = "G, P, P";
    eType           = ForEdit;
}

void CmdSketcherCompCreateRegularPolygon::activated(int iMsg)
{
    switch( iMsg ){
    case 0:
        ActivateHandler(getActiveGuiDocument(),new DrawSketchHandlerRegularPolygon(3)); break;
    case 1:
        ActivateHandler(getActiveGuiDocument(),new DrawSketchHandlerRegularPolygon(4)); break;
    case 2:
        ActivateHandler(getActiveGuiDocument(),new DrawSketchHandlerRegularPolygon(5)); break;
    case 3:
        ActivateHandler(getActiveGuiDocument(),new DrawSketchHandlerRegularPolygon(6)); break;
    case 4:
        ActivateHandler(getActiveGuiDocument(),new DrawSketchHandlerRegularPolygon(7)); break;
    case 5:
        ActivateHandler(getActiveGuiDocument(),new DrawSketchHandlerRegularPolygon(8)); break;
    case 6:
    {
        // Pop-up asking for values
        /*SketcherRegularPolygonDialog srpd;
        if (srpd.exec() == QDialog::Accepted)
            ActivateHandler(getActiveGuiDocument(),new DrawSketchHandlerRegularPolygon(srpd.sides));*/
            ActivateHandler(getActiveGuiDocument(),new DrawSketchHandlerRegularPolygon(1));
    }
    break;
    default:
        return;
    }

    // Since the default icon is reset when enabling/disabling the command we have
    // to explicitly set the icon of the used command.
    Gui::ActionGroup* pcAction = dynamic_cast<Gui::ActionGroup*>(_pcAction);
    QList<QAction*> a = pcAction->actions();

    assert(iMsg < a.size());
    pcAction->setIcon(a[iMsg]->icon());
}

Gui::Action * CmdSketcherCompCreateRegularPolygon::createAction(void)
{
    Gui::ActionGroup* pcAction = new Gui::ActionGroup(this, Gui::getMainWindow());
    pcAction->setDropDownMenu(true);
    applyCommandData(this->className(), pcAction);

    QAction* triangle = pcAction->addAction(QString());
    triangle->setIcon(Gui::BitmapFactory().iconFromTheme("Sketcher_CreateTriangle"));
    QAction* square = pcAction->addAction(QString());
    square->setIcon(Gui::BitmapFactory().iconFromTheme("Sketcher_CreateSquare"));
    QAction* pentagon = pcAction->addAction(QString());
    pentagon->setIcon(Gui::BitmapFactory().iconFromTheme("Sketcher_CreatePentagon"));
    QAction* hexagon = pcAction->addAction(QString());
    hexagon->setIcon(Gui::BitmapFactory().iconFromTheme("Sketcher_CreateHexagon"));
    QAction* heptagon = pcAction->addAction(QString());
    heptagon->setIcon(Gui::BitmapFactory().iconFromTheme("Sketcher_CreateHeptagon"));
    QAction* octagon = pcAction->addAction(QString());
    octagon->setIcon(Gui::BitmapFactory().iconFromTheme("Sketcher_CreateOctagon"));
    QAction* regular = pcAction->addAction(QString());
    regular->setIcon(Gui::BitmapFactory().iconFromTheme("Sketcher_CreateRegularPolygon"));

    _pcAction = pcAction;
    languageChange();

    pcAction->setIcon(hexagon->icon());
    int defaultId = 3;
    pcAction->setProperty("defaultAction", QVariant(defaultId));

    return pcAction;
}

void CmdSketcherCompCreateRegularPolygon::updateAction(int mode)
{
    Gui::ActionGroup* pcAction = dynamic_cast<Gui::ActionGroup*>(getAction());
    if (!pcAction)
        return;

    QList<QAction*> a = pcAction->actions();
    int index = 0;// = pcAction->property("defaultAction").toInt();
    switch (mode) {
    case Normal:
        a[0]->setIcon(Gui::BitmapFactory().iconFromTheme("Sketcher_CreateTriangle"));
        a[1]->setIcon(Gui::BitmapFactory().iconFromTheme("Sketcher_CreateSquare"));
        a[2]->setIcon(Gui::BitmapFactory().iconFromTheme("Sketcher_CreatePentagon"));
        a[3]->setIcon(Gui::BitmapFactory().iconFromTheme("Sketcher_CreateHexagon"));
        a[4]->setIcon(Gui::BitmapFactory().iconFromTheme("Sketcher_CreateHeptagon"));
        a[5]->setIcon(Gui::BitmapFactory().iconFromTheme("Sketcher_CreateOctagon"));
        a[6]->setIcon(Gui::BitmapFactory().iconFromTheme("Sketcher_CreateRegularPolygon"));
        getAction()->setIcon(a[index]->icon());
        break;
    case Construction:
        a[0]->setIcon(Gui::BitmapFactory().iconFromTheme("Sketcher_CreateTriangle_Constr"));
        a[1]->setIcon(Gui::BitmapFactory().iconFromTheme("Sketcher_CreateSquare_Constr"));
        a[2]->setIcon(Gui::BitmapFactory().iconFromTheme("Sketcher_CreatePentagon_Constr"));
        a[3]->setIcon(Gui::BitmapFactory().iconFromTheme("Sketcher_CreateHexagon_Constr"));
        a[4]->setIcon(Gui::BitmapFactory().iconFromTheme("Sketcher_CreateHeptagon_Constr"));
        a[5]->setIcon(Gui::BitmapFactory().iconFromTheme("Sketcher_CreateOctagon_Constr"));
        a[6]->setIcon(Gui::BitmapFactory().iconFromTheme("Sketcher_CreateRegularPolygon_Constr"));
        getAction()->setIcon(a[index]->icon());
        break;
    }
}

void CmdSketcherCompCreateRegularPolygon::languageChange()
{
    Command::languageChange();

    if (!_pcAction)
        return;
    Gui::ActionGroup* pcAction = dynamic_cast<Gui::ActionGroup*>(_pcAction);
    QList<QAction*> a = pcAction->actions();

    QAction* triangle = a[0];
    triangle->setText(QApplication::translate("CmdSketcherCompCreateRegularPolygon","Triangle"));
    triangle->setToolTip(QApplication::translate("Sketcher_CreateTriangle","Create an equilateral triangle by its center and by one corner"));
    triangle->setStatusTip(QApplication::translate("Sketcher_CreateTriangle","Create an equilateral triangle by its center and by one corner"));
    QAction* square = a[1];
    square->setText(QApplication::translate("CmdSketcherCompCreateRegularPolygon","Square"));
    square->setToolTip(QApplication::translate("Sketcher_CreateSquare","Create a square by its center and by one corner"));
    square->setStatusTip(QApplication::translate("Sketcher_CreateSquare","Create a square by its center and by one corner"));
    QAction* pentagon = a[2];
    pentagon->setText(QApplication::translate("CmdSketcherCompCreateRegularPolygon","Pentagon"));
    pentagon->setToolTip(QApplication::translate("Sketcher_CreatePentagon","Create a pentagon by its center and by one corner"));
    pentagon->setStatusTip(QApplication::translate("Sketcher_CreatePentagon","Create a pentagon by its center and by one corner"));
    QAction* hexagon = a[3];
    hexagon->setText(QApplication::translate("CmdSketcherCompCreateRegularPolygon","Hexagon"));
    hexagon->setToolTip(QApplication::translate("Sketcher_CreateHexagon","Create a hexagon by its center and by one corner"));
    hexagon->setStatusTip(QApplication::translate("Sketcher_CreateHexagon","Create a hexagon by its center and by one corner"));
    QAction* heptagon = a[4];
    heptagon->setText(QApplication::translate("CmdSketcherCompCreateRegularPolygon","Heptagon"));
    heptagon->setToolTip(QApplication::translate("Sketcher_CreateHeptagon","Create a heptagon by its center and by one corner"));
    heptagon->setStatusTip(QApplication::translate("Sketcher_CreateHeptagon","Create a heptagon by its center and by one corner"));
    QAction* octagon = a[5];
    octagon->setText(QApplication::translate("CmdSketcherCompCreateRegularPolygon","Octagon"));
    octagon->setToolTip(QApplication::translate("Sketcher_CreateOctagon","Create an octagon by its center and by one corner"));
    octagon->setStatusTip(QApplication::translate("Sketcher_CreateOctagon","Create an octagon by its center and by one corner"));
    QAction* regular = a[6];
    regular->setText(QApplication::translate("CmdSketcherCompCreateRegularPolygon","Regular polygon"));
    regular->setToolTip(QApplication::translate("Sketcher_CreateOctagon","Create a regular polygon by its center and by one corner"));
    regular->setStatusTip(QApplication::translate("Sketcher_CreateOctagon","Create a regular polygon by its center and by one corner"));
}

bool CmdSketcherCompCreateRegularPolygon::isActive(void)
{
    return isCreateGeoActive(getActiveGuiDocument());
}

void CreateSketcherCommandsCreateGeo(void)
{
    Gui::CommandManager &rcCmdMgr = Gui::Application::Instance->commandManager();

    rcCmdMgr.addCommand(new CmdSketcherCreatePoint());
    rcCmdMgr.addCommand(new CmdSketcherCreateArc());
    rcCmdMgr.addCommand(new CmdSketcherCreate3PointArc());
    rcCmdMgr.addCommand(new CmdSketcherCompCreateArc());
    rcCmdMgr.addCommand(new CmdSketcherCreateCircle());
    rcCmdMgr.addCommand(new CmdSketcherCreate3PointCircle());
    rcCmdMgr.addCommand(new CmdSketcherCompCreateCircle());
    rcCmdMgr.addCommand(new CmdSketcherCreateEllipseByCenter());
    rcCmdMgr.addCommand(new CmdSketcherCreateEllipseBy3Points());
    rcCmdMgr.addCommand(new CmdSketcherCompCreateConic());
    rcCmdMgr.addCommand(new CmdSketcherCreateArcOfEllipse());
    rcCmdMgr.addCommand(new CmdSketcherCreateArcOfHyperbola());
    rcCmdMgr.addCommand(new CmdSketcherCreateArcOfParabola());
    rcCmdMgr.addCommand(new CmdSketcherCreateBSpline());
    rcCmdMgr.addCommand(new CmdSketcherCreatePeriodicBSpline());
    rcCmdMgr.addCommand(new CmdSketcherCompCreateBSpline());
    rcCmdMgr.addCommand(new CmdSketcherCreateLine());
    rcCmdMgr.addCommand(new CmdSketcherCreatePolyline());
    rcCmdMgr.addCommand(new CmdSketcherCreateRectangle());
    rcCmdMgr.addCommand(new CmdSketcherCreateRectangleCenter());
    rcCmdMgr.addCommand(new CmdSketcherCreateOblong());
    rcCmdMgr.addCommand(new CmdSketcherCompCreateRegularPolygon());
    rcCmdMgr.addCommand(new CmdSketcherCreateTriangle());
    rcCmdMgr.addCommand(new CmdSketcherCreateSquare());
    rcCmdMgr.addCommand(new CmdSketcherCreatePentagon());
    rcCmdMgr.addCommand(new CmdSketcherCreateHexagon());
    rcCmdMgr.addCommand(new CmdSketcherCreateHeptagon());
    rcCmdMgr.addCommand(new CmdSketcherCreateOctagon());
    rcCmdMgr.addCommand(new CmdSketcherCreateRegularPolygon());
    rcCmdMgr.addCommand(new CmdSketcherCompCreateRectangles());
    rcCmdMgr.addCommand(new CmdSketcherCreateSlot());
    rcCmdMgr.addCommand(new CmdSketcherCompCreateFillets());
    rcCmdMgr.addCommand(new CmdSketcherCreateFillet());
    rcCmdMgr.addCommand(new CmdSketcherCreatePointFillet());
    //rcCmdMgr.addCommand(new CmdSketcherCreateText());
    //rcCmdMgr.addCommand(new CmdSketcherCreateDraftLine());
    rcCmdMgr.addCommand(new CmdSketcherTrimming());
    rcCmdMgr.addCommand(new CmdSketcherExtend());
    rcCmdMgr.addCommand(new CmdSketcherSplit());
    rcCmdMgr.addCommand(new CmdSketcherExternal());
    rcCmdMgr.addCommand(new CmdSketcherCarbonCopy());
}
